Zum Hauptinhalt springen

CooLWJ durchsuchen

选择搜索范围,输入关键词后按回车。

Sprache

Einführung in die Verwendung von Spring Cache

I Caching-Lösung auf Basis von SpringCache

1.1 Einführung in SpringCache

springcache wurde mit spring3.1 veröffentlicht. Es kapselt und abstrahiert die Verwendung von Caches, sodass sich zwischengespeicherte Ergebnisse durch Annotationen an Methoden abrufen lassen. Gerade durch den Einsatz von Annotationen löst es das Problem der Kopplung zwischen Geschäftslogik und Cache-Code: Bestehender Code unterstützt sofort Caching, ohne dass in die Geschäftslogik eingegriffen werden muss. Entwickler können den Cache dadurch transparent nutzen.

1.2 Überblick über SpringCache

Es gibt viele Caching-Frameworks mit jeweils eigenen Stärken, beispielsweise Redis, Memcached, Guava und Caffeine.
Wenn unser Programm einen Cache nutzen möchte, muss es an diese Frameworks gekoppelt werden. Kluge Softwarearchitekten nutzen bereits Schnittstellen, um diese Kopplung zu verringern. Mithilfe objektorientierter Abstraktion und Polymorphie trennen sie den Geschäftscode vom konkreten Framework.
Wir müssen jedoch weiterhin ausdrücklich im Code die Schnittstellen und Methoden für den Cache aufrufen, zum passenden Zeitpunkt Daten in den Cache einfügen und zum passenden Zeitpunkt Daten daraus lesen. Denken wir einmal darüber nach「AOP」 als Einsatzszenario – ist das nicht geradezu wie geschaffen für AOP?
Seit Spring 3.1 gibt es annotationsbasierte Cache-Unterstützung, ähnlich der Transaktionsverwaltung mit der Annotation [@Transactional](/Transactional ), sowie eine Cache-Abstraktion. Zuvor wurde dies in der Regel über AOP umgesetzt.
Vorteile der Verwendung von Spring Cache:

  • Stellt eine grundlegende Cache-Abstraktion bereit, die den Wechsel zwischen verschiedenen zugrunde liegenden Caches erleichtert;

  • Mit Cache-Annotationen lässt sich die Cache-Logik ähnlich wie bei Transaktionen transparent auf unseren Geschäftscode anwenden, und dafür ist nur wenig zusätzlicher Code nötig;

  • Sorgt dafür, dass beim Zurückrollen einer Transaktion auch der Cache automatisch zurückgerollt wird;

  • Unterstützt komplexere Caching-Logik;

Technisches Kennzeichen von Spring Cache

1.3 Grundlegendes Design von Spring Cache

Mit Spring 3.1 wurde Spring Cache eingeführt, die Cache-Abstraktion von Spring. Durch die Definition von org.springframework.cache.Cache  und org.springframework.cache.CacheManager Schnittstelle, um verschiedene Cache-Technologien zu vereinheitlichen, und unterstützt JCache-Annotationen zur Vereinfachung der Entwicklung.
Die Cache-Schnittstelle definiert die Spezifikation für Cache-Komponenten und umfasst die verschiedenen Cache-Operationen. Spring stellt für die Cache-Schnittstelle verschiedene xxxCache  Implementierung: RedisCache , EhCacheCache , ConcurrentMapCache usw.
Sehen wir uns anhand einiger Quellcodeauszüge die Cache-Schnittstelle und CacheManager  Schnittstelle.

Cache-Schnittstelle

public interface Cache {     //Cache名称     String getName();      //Cache负责缓存的对象     Object getNativeCache();      /**      * 获取key对应的ValueWrapper      * 没有对应的key,则返回null      * key对应的value是null,则返回null对应的ValueWrapper      */     @Nullable     Cache.ValueWrapper get(Object key);      //返回key对应type类型的value     @Nullable     <T> T get(Object key, @Nullable Class<T> type);      //返回key对应的value,没有则缓存Callable::call,并返回     @Nullable     <T> T get(Object key, Callable<T> valueLoader);      //缓存目标key-value(替换旧值),不保证实时性     void put(Object key, @Nullable Object value);      //插入缓存,并返回该key对应的value;先调用get,不存在则用put实现     @Nullable     default Cache.ValueWrapper putIfAbsent(Object key, @Nullable Object value) {         Cache.ValueWrapper existingValue = this.get(key);         if (existingValue == null) {             this.put(key, value);         }          return existingValue;     }      //删除缓存,不保证实时性     void evict(Object key);      //立即删除缓存:返回false表示剔除前不存在制定key活不确定是否存在;返回true,表示该key之前存在     default boolean evictIfPresent(Object key) {         this.evict(key);         return false;     }      //清除所有缓存,不保证实时性     void clear();      //立即清楚所有缓存,返回false表示清除前没有缓存或不能确定是否有;返回true表示清除前有缓存     default boolean invalidate() {         this.clear();         return false;     }      public static class ValueRetrievalException extends RuntimeException {         @Nullable         private final Object key;          public ValueRetrievalException(@Nullable Object key, Callable<?> loader, Throwable ex) {             super(String.format("Value for key '%s' could not be loaded using '%s'", key, loader), ex);             this.key = key;         }          @Nullable         public Object getKey() {             return this.key;         }     }      //缓存值的一个包装器接口,实现类为SimpleValueWrapper     @FunctionalInterface     public interface ValueWrapper {         @Nullable         Object get();     } }

Wie man sieht, abstrahiert die Cache-Schnittstelle die zugehörigen Cache-Operationen wie get, put und evict.

Abstrakte Klasse AbstractValueAdaptingCache

public abstract class AbstractValueAdaptingCache implements Cache {      //是否允许Null值     private final boolean allowNullValues;      protected AbstractValueAdaptingCache(boolean allowNullValues) {         this.allowNullValues = allowNullValues;     }      public final boolean isAllowNullValues() {         return this.allowNullValues;     }      @Nullable     public ValueWrapper get(Object key) {         return this.toValueWrapper(this.lookup(key));     }      @Nullable     public <T> T get(Object key, @Nullable Class<T> type) {         //查询到的缓存值做fromStoreValue转换         Object value = this.fromStoreValue(this.lookup(key));         //转换后非null值且无法转换为type类型则抛出异常         if (value != null && type != null && !type.isInstance(value)) {             throw new IllegalStateException("Cached value is not of required type [" + type.getName() + "]: " + value);         } else {             return value;         }     }      //从缓存中获取key对应的value,子类实现     @Nullable     protected abstract Object lookup(Object key);      //对于从缓存中获取的值,允许为空且值为NullValue时,处理为null     @Nullable     protected Object fromStoreValue(@Nullable Object storeValue) {         return this.allowNullValues && storeValue == NullValue.INSTANCE ? null : storeValue;     }      //对于要插入缓存的null值,在允许null值的情况下处理为NullValue;否则抛出异常IllegalArgumentException     protected Object toStoreValue(@Nullable Object userValue) {         if (userValue == null) {             if (this.allowNullValues) {                 return NullValue.INSTANCE;             } else {                 throw new IllegalArgumentException("Cache '" + this.getName() + "' is configured to not allow null values but null was provided");             }         } else {             return userValue;         }     }      //get操作依据,查询到缓存值非null,则fromStoreValue转换后包装成SimpleValueWrapper返回     @Nullable     protected ValueWrapper toValueWrapper(@Nullable Object storeValue) {         return storeValue != null ? new SimpleValueWrapper(this.fromStoreValue(storeValue)) : null;     } }

Abstrakte Klasse AbstractValueAdaptingCache implementiert die Cache-Schnittstelle und abstrahiert hauptsächlich die Logik zur Behandlung von NULL-Werten.

  • allowNullValues Das Attribut gibt an, ob das Zwischenspeichern von NULL-Werten zulässig ist

  • fromStoreValue der Methode zur Behandlung von NULL-Werten get Operation, in der Eigenschaft allowNullValues bei true wird NullValue als NULL behandelt

  • toStoreValue  Methode zur Behandlung von NULL-Werten put Operation, in der Eigenschaft allowNullValues Bei true wird NULL als NullValue behandelt, andernfalls wird eine Ausnahme ausgelöst

  • toValueWrapper Methode stellt bereit Cache in der Schnittstelle get Die Standardimplementierung der Methode liest den Wert aus dem Cache und anschließend über fromStoreValue umwandeln und schließlich verpacken als SimpleValueWrapper Zurück

  • ValueWrapper get(Object key)  und T get(Object key, @Nullable Classtype) Die Methode basiert auf der oben beschriebenen Methode

  • ValueWrapper get(Object key)  und @Nullable Classtype) Die Methode basiert auf der oben beschriebenen Methode

  • lookup Die abstrakte Methode dient dazu, Unterklassen den tatsächlichen Cache-Wert abrufen zu lassen

ConcurrentMapCache

public class ConcurrentMapCache extends AbstractValueAdaptingCache {     private final String name;     //定义ConcurrentMap缓存     private final ConcurrentMap<Object, Object> store;     //如果要缓存的是值对象的copy,则由此序列化代理类处理     @Nullable     private final SerializationDelegate serialization;      public ConcurrentMapCache(String name) {         this(name, new ConcurrentHashMap(256), true);     }      //默认允许处理null     public ConcurrentMapCache(String name, boolean allowNullValues) {         this(name, new ConcurrentHashMap(256), allowNullValues);     }      //默认serialization = null     public ConcurrentMapCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues) {         this(name, store, allowNullValues, (SerializationDelegate)null);     }      protected ConcurrentMapCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues, @Nullable SerializationDelegate serialization) {         super(allowNullValues);         Assert.notNull(name, "Name must not be null");         Assert.notNull(store, "Store must not be null");         this.name = name;         this.store = store;         this.serialization = serialization;     }      //serialization不为空,缓存值对象的copy     public final boolean isStoreByValue() {         return this.serialization != null;     }      public final String getName() {         return this.name;     }      public final ConcurrentMap<Object, Object> getNativeCache() {         return this.store;     }      //实现lookup:store#get     @Nullable     protected Object lookup(Object key) {         return this.store.get(key);     }      //基于ConcurrentMap::computeIfAbsent方法实现;get和put的值由fromStoreValue和toStoreValue处理Null     @Nullable     public <T> T get(Object key, Callable<T> valueLoader) {         return this.fromStoreValue(this.store.computeIfAbsent(key, (k) -> {             try {                 return this.toStoreValue(valueLoader.call());             } catch (Throwable var5) {                 throw new ValueRetrievalException(key, valueLoader, var5);             }         }));     }      public void put(Object key, @Nullable Object value) {         this.store.put(key, this.toStoreValue(value));     }      @Nullable     public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {         Object existing = this.store.putIfAbsent(key, this.toStoreValue(value));         return this.toValueWrapper(existing);     }      public void evict(Object key) {         this.store.remove(key);     }      public boolean evictIfPresent(Object key) {         return this.store.remove(key) != null;     }      public void clear() {         this.store.clear();     }      public boolean invalidate() {         boolean notEmpty = !this.store.isEmpty();         this.store.clear();         return notEmpty;     }      protected Object toStoreValue(@Nullable Object userValue) {         Object storeValue = super.toStoreValue(userValue);         if (this.serialization != null) {             try {                 return this.serialization.serializeToByteArray(storeValue);             } catch (Throwable var4) {                 throw new IllegalArgumentException("Failed to serialize cache value '" + userValue + "'. Does it implement Serializable?", var4);             }         } else {             return storeValue;         }     }      protected Object fromStoreValue(@Nullable Object storeValue) {         if (storeValue != null && this.serialization != null) {             try {                 return super.fromStoreValue(this.serialization.deserializeFromByteArray((byte[])((byte[])storeValue)));             } catch (Throwable var3) {                 throw new IllegalArgumentException("Failed to deserialize cache value '" + storeValue + "'", var3);             }         } else {             return super.fromStoreValue(storeValue);         }     } }

ConcurrentMapCache erbt von der abstrakten Klasse AbstractValueAdaptingCache ,ist die standardmäßige Cache-Implementierung von Spring. Sie unterstützt das Zwischenspeichern von Kopien der Cache-Objekte; die Serialisierung übernimmt SerializationDelegate serialization. Der Standardwert ist null, was einem referenzbasierten Cache entspricht. Die Cache-Operationen beruhen auf der Basisklasse AbstractValueAdaptingCache Behandlung von null-Werten bei , standardmäßig ist null zulässig.

CacheManager

public interface CacheManager {     @Nullable     //获取指定name的Cache,可能延迟创建     Cache getCache(String name);      //获取当前CacheManager下的Cache name集合     Collection<String> getCacheNames(); }

CacheManager Verwaltet anhand von name eine Gruppe von Cache-Instanzen. Natürlich, CacheManager hat ebenfalls viele Implementierungsklassen, etwa ConcurrentMapCacheManager 、 AbstractCacheManager und SimpleCacheManager , diese xxxCacheManager Klassen dienen alle dazu, Regeln für die Cache-Verwaltung festzulegen; darauf gehen wir hier nicht weiter ein.

Zwei SpringCache verwenden

2.1 Verwendung

Spring Cache ist ein solches Framework. Es nutzt AOP, um annotationsbasierte Cache-Funktionen bereitzustellen, und bietet eine sinnvolle Abstraktion. Der Geschäftscode muss sich nicht darum kümmern, welches Cache-Framework darunter verwendet wird. Eine einfache Annotation genügt, um die Cache-Funktion zu aktivieren.

Abhängigkeit hinzufügen

<!--springboot的cache支持--> <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-cache</artifactId> </dependency>  <!--配置拓展还需添加的依赖--> <dependency>   <groupId>com.github.ben-manes.caffeine</groupId>   <artifactId>caffeine</artifactId>   <version>2.9.2</version> </dependency>

Cache konfigurieren

Die Annotation @EnableCaching zur Application-Startklasse hinzufügen

@SpringBootApplication     @MapperScan("com.xxxx.dao")     @EnableCaching     public class AxxxxApplication {          public static void main(String[] args) {             SpringApplication.run(AxxxxApplication.class, args);         }      }

Den Cache-Typ von cache konfigurieren; hier wird hauptsächlich der lokale Cache vorgestellt

//方式一:ConcurrentMapCache (不好设置过期时间) @Configuration public class SpringCachingConfig {        @Bean      public CacheManager cacheManager() {           SimpleCacheManager cacheManager = new SimpleCacheManager();           Set<ConcurrentMapCache> mapCaches=new HashSet<>();           //配置cache类型与目录           mapCaches.add(new ConcurrentMapCache("cacheNames1"));           mapCaches.add(new ConcurrentMapCache("cacheNames2"));           mapCaches.add(new ConcurrentMapCache("cacheNames3"));           mapCaches.add(new ConcurrentMapCache("cacheNames4"));           cacheManager.setCaches(mapCaches);           cacheManager.afterPropertiesSet();           return cacheManager;      }   }     //方式二:CaffeineCache (可设置过期时间) @Configuration public class SpringCachingConfig {        @Bean     public CacheManager cacheManager() {         SimpleCacheManager cacheManager = new SimpleCacheManager();           List<CaffeineCache> caffeineCaches = new ArrayList<>();           //SysConstants.CACHE_NAMES=new ArrayList(         //Arrays.asList("cacheNames1","cacheNames2","cacheNames3","cacheNames4")         //);         for (String  cacheName: SysConstants.CACHE_NAMES) {             caffeineCaches.add(new CaffeineCache(cacheName,                     Caffeine.newBuilder()                             .initialCapacity(50)                             .maximumSize(30000)//最大存储数量                             .expireAfterWrite(2, TimeUnit.HOURS)//过期时间2 小时                             .build()));         }         cacheManager.setCaches(caffeineCaches);         return cacheManager;     }   }     //方式三。。。。还可以配置redis以及其他的方式,这里主要介绍以上两种方式

Caching für Schnittstellen implementieren

//控制器1 @RestController @RequestMapping("/path1") @Cacheable(cacheNames = "cacheNames1" ,key="#root.methodName+#a0" ) public class Controller1 {   }   //控制器2 @RestController @RequestMapping("/path2") @Cacheable(cacheNames = "cacheNames2" ,key="#root.methodName+#a0" ) public class Controller2 {   }   //控制器3 @RestController @RequestMapping("/path3") @Cacheable(cacheNames = "cacheNames3" ,key="#root.methodName+#a0" ) public class Controller3 {   }   //控制器4 @RestController @RequestMapping("/path4") @Cacheable(cacheNames = "cacheNames4" ,key="#root.methodName+#a0" ) public class Controller4 {   }

2.2 Ausführliche Erläuterung der SpringCache-Annotationen

Die Annotation @Cacheable

Falls nicht im Cache vorhanden:Datenbank abfragen, im Cache speichern, Ergebnis zurückgeben,
Falls im Cache vorhanden: Ergebnis direkt zurückgeben

Parameter Erläuterung Beispiel
Wert Die Namen der Caches, die in der spring- Konfigurationsdatei definiert sind. Mindestens einer muss angegeben werden Zum Beispiel:
@Cacheable(value=”mycache”)
@Cacheable(value={”cache1”,”cache2”}
Schlüssel Der Schlüssel des Caches; kann leer bleiben. Wird er angegeben, muss er als SpEL-Ausdruck formuliert werden. Ohne Angabe werden standardmäßig alle Methodenparameter kombiniert. @Cacheable(value=”testcache”,key=”#userName”)
condition Bedingung für das Caching, kann leer sein, wird in SpEL geschrieben und liefert true oder false zurück; nur bei true wird gecacht @Cacheable(value=”testcache”,condition=”#userName.length()>2”)

Funktion: Damit lassen sich Ergebnisse in den Cache schreiben und dort speichern, sodass bei späteren Aufrufen direkt der zwischengespeicherte Wert zurückgegeben werden kann, ohne die eigentliche Methode erneut auszuführen. Die einfachste Verwendung, Annotationsname=Cache-Name, zeigt das folgende Beispiel:

@Cacheable(value="accountCache")// 使用了一个缓存名叫 accountCache  public Account getAccountByName(String userName) {    // 方法内部实现不考虑缓存逻辑,直接实现业务    System.out.println("real query account."+userName);     return getFromDB(userName);  }

Die Annotation @CacheEvict

@CacheEvict:Annotation zum Löschen von Cache-Einträgen, die beim Entfernen alter und nutzloser Daten sehr hilfreich ist. Hier gibt es außerdem den Parameter (allEntries); mit allEntries=true lassen sich sämtliche Einträge gesammelt löschen

Parameter Erläuterung Beispiel
Wert Die Namen der Caches, die in der spring- Konfigurationsdatei definiert sind. Mindestens einer muss angegeben werden @CacheEvict(value=”my cache”)
Schlüssel Der Schlüssel des Caches; kann leer bleiben. Wird er angegeben, muss er als SpEL-Ausdruck formuliert werden. Ohne Angabe werden standardmäßig alle Methodenparameter kombiniert. @CacheEvict(value=”testcache”,key=”#userName”)
condition Bedingung für das Caching, kann leer sein, wird in SpEL geschrieben und liefert true oder false zurück; nur bei true wird gecacht @CacheEvict(value=”testcache”,condition=”#userName.length()>2”)
allEntries Ob sämtliche Cache-Inhalte gelöscht werden sollen, standardmäßig false; wird true angegeben, werden unmittelbar nach dem Methodenaufruf sämtliche Cache-Inhalte gelöscht @CachEvict(value=”testcache”,allEntries=true)
beforeInvocation Ob der Cache bereits vor der Ausführung der Methode geleert wird, standardmäßig false. Wird true angegeben, wird der Cache geleert, bevor die Methode ausgeführt wird. Standardmäßig wird der Cache nicht geleert, wenn die Methodenausführung eine Ausnahme auslöst @CachEvict(value=”testcache”,beforeInvocation=true)

 

@CacheEvict(value="accountCache",key="#account.getName()")// 清空accountCache 缓存  public void updateAccount(Account account) {    updateDB(account);  }   @CacheEvict(value="accountCache",allEntries=true)// 清空accountCache 缓存 public void reload() {    reloadAll() }  @Cacheable(value="accountCache",condition="#userName.length() <=4")// 缓存名叫 accountCache  public Account getAccountByName(String userName) {   // 方法内部实现不考虑缓存逻辑,直接实现业务  return getFromDB(userName);  }

@CachePut-Annotation

@CachePut:Diese Annotation kann verwendet werden, wenn der Cache aktualisiert werden soll, ohne die Ausführung der Methode zu beeinträchtigen. Das heißt, die Methode wird immer ausgeführt und ihr Ergebnis im Cache abgelegt;

Parameter Erläuterung Beispiel
Wert Die Namen der Caches, die in der spring- Konfigurationsdatei definiert sind. Mindestens einer muss angegeben werden @CachePut(value=”my cache”)
Schlüssel Der Schlüssel des Caches; kann leer bleiben. Wird er angegeben, muss er als SpEL-Ausdruck formuliert werden. Ohne Angabe werden standardmäßig alle Methodenparameter kombiniert. @CachePut(value=”testcache”,key=”#userName”)
condition Bedingung für das Caching, kann leer sein, wird in SpEL geschrieben und liefert true oder false zurück; nur bei true wird gecacht @CachePut(value=”testcache”,condition=”#userName.length()>2”)

Die Annotation @CachePut stellt sicher, dass die Methode ausgeführt und gleichzeitig ihr Rückgabewert im Cache gespeichert wird, wodurch Cache und Datenbank synchron aktualisiert werden.

@CachePut(value="accountCache",key="#account.getName()")// 更新accountCache 缓存 public Account updateAccount(Account account) {    return updateDB(account);  }

@Caching-Annotation

Bei der Verwendung eines Caches kann es vorkommen, dass gleichzeitig aktualisiert und gelöscht wird und daher mehrere Annotationen gleichzeitig zum Einsatz kommen. Mit @Caching lässt sich dies umsetzen

//添加user缓存的同时,移除userPage的缓存 @Caching(     put =@CachePut(value = "user",key ="#userVo.id"), evict = @CacheEvict(value = "userPage",allEntries = true) )
@Caching(put = { @CachePut(value = "user", key = "#user.id"), @CachePut(value = "user", key = "#user.username"), @CachePut(value = "user", key = "#user.email") }) public User save(User user) {

@CacheConfig

Jede @Cacheable()-Annotation enthält ein Attribut value=“xxx”. Bei vielen Methoden ist das Schreiben offensichtlich ziemlich mühsam. Wenn man alles einmal zentral deklarieren könnte, wäre es einfacher. Deshalb gibt es die Konfiguration @CacheConfig. @CacheConfig ist eine Annotation auf Klassenebene, mit der Cache-Namen gemeinsam genutzt werden können. Wenn Sie in einer Methode einen anderen Namen angeben, hat weiterhin der dort angegebene Name Vorrang.

@CacheConfig("books") public class BookRepositoryImpl implements BookRepository {    @Cacheable   public Book findBook(ISBN isbn) {...} }

Bedingtes Caching
Im Folgenden einige häufig verwendete Bedingungen für das Caching

//@Cacheable将在执行方法之前( #result还拿不到返回值)判断condition,如果返回true,则查缓存;  @Cacheable(value = "user", key = "#id", condition = "#id lt 10") public User conditionFindById(final Long id)   //@CachePut将在执行完方法后(#result就能拿到返回值了)判断condition,如果返回true,则放入缓存;  @CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'")  public User conditionSave(final User user)    //@CachePut将在执行完方法后(#result就能拿到返回值了)判断unless,如果返回false,则放入缓存;(即跟condition相反) @CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'") public User conditionSave2(final User user)    //@CacheEvict, beforeInvocation=false表示在方法执行之后调用(#result能拿到返回值了);且判断condition,如果返回true,则移除缓存; @CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'")  public User conditionDelete(final User user)

Benutzerdefinierte Cache-Annotation

Die zuvor gezeigte @Caching-Kombination etwa lässt den Code durch die Annotationen an der Methode recht unübersichtlich wirken. In diesem Fall kann man diese Annotationen in einer benutzerdefinierten Annotation zusammenfassen, zum Beispiel:

@Caching(put = { @CachePut(value = "user", key = "#user.id"), @CachePut(value = "user", key = "#user.username"), @CachePut(value = "user", key = "#user.email") }) @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface UserSaveCache { }

So müssen wir lediglich den folgenden Code an der Methode verwenden, wodurch der gesamte Code recht übersichtlich wirkt.

@UserSaveCache public User save(User user)

Erweiterung
Bei findByUsername sollte beispielsweise nicht nur username–>user gespeichert werden, sondern zugleich auch id—>user und email—>user; so ergibt sich beim nächsten Suchen nach der ID direkt ein Cache-Treffer

@Caching(   cacheable = {     @Cacheable(value = "user", key = "#username")   },   put = {     @CachePut(value = "user", key = "#result.id", condition = "#result != null"),     @CachePut(value = "user", key = "#result.email", condition = "#result != null")   } ) public User findByUsername(final String username) {   System.out.println("cache miss, invoke find by username, username:" + username);   for (User user : users) {     if (user.getUsername().equals(username)) {       return user;     }   }   return null; }

Tatsächlich wäre für:id—>user;username—->user;email—>user;möglicherweise folgende Vorgehensweise besser:id—>user;username—>id;email—>id;so wird user nur einmal gespeichert;zum Beispiel:

@CachePut(value="cacheName", key="#user.username", cacheValue="#user.username")  public void save(User user)     @Cacheable(value="cacheName", key="#user.username", cacheValue="#caches[0].get(#caches[0].get(#username).get())")  public User findByUsername(String username)

SpEL-Kontextdaten
Spring Cache stellt uns einige SpEL-Kontextdaten zur Verfügung. Die folgende Tabelle wurde direkt aus der offiziellen Spring-Dokumentation übernommen:

Name Position Beschreibung Beispiel
methodName root-Objekt Name der aktuell aufgerufenen Methode root.methodName
Methode root-Objekt Die aktuell aufgerufene Methode root.method.name
target root-Objekt Das Zielobjekt, das gerade aufgerufen wird root.target
targetClass root-Objekt Klasse des Zielobjekts, das gerade aufgerufen wird root.targetClass
args root-Objekt Parameterliste der aktuell aufgerufenen Methode root.args[0]
caches root-Objekt Die Liste der beim aktuellen Methodenaufruf verwendeten Caches(z. B. @Cacheable(value={“cache1”, “cache2”})), hier sind es zwei Caches root.caches[0].name
Argumentname Ausführungskontext Die Parameter der aktuell aufgerufenen Methode, etwa findById(Long id), lassen sich über #id abrufen user.id
Ergebnis Ausführungskontext Der Rückgabewert nach der Methodenausführung(nur bei Bedingungen verfügbar, die nach der Methodenausführung ausgewertet werden, etwa bei ‘unless' oder bei 'cache evict' mit beforeInvocation=false) Ergebnis
@CacheEvict(value = "user", key = "#user.id", condition = "#root.target.canCache() and #root.caches[0].get(#user.id).get().username ne #user.username", beforeInvocation = true)  public void conditionUpdate(User user)

Zwischenfazit

Für die deklarative Cache-Konfiguration stellt Spring eine Reihe von Java-Annotationen bereit:
· @Cacheable
o Funktion: Löst das Schreiben in den Cache aus. Wenn kein Eintrag im Cache vorhanden ist, wird die Datenbank abgefragt, das Ergebnis im Cache gespeichert und zurückgegeben. Ist ein Eintrag im Cache vorhanden, wird das Ergebnis direkt zurückgegeben
o Anwendung:Die Datenbank abfragen Methode
· @CacheEvict
o Funktion: Löscht den Cache
o Anwendung:Datenbank löschen oder ändern Methode
· @CachePut
o Funktion: Schreiben in den Cache (beeinflusst die Ausführung der Methode nicht).
o Anwendung:Zur Datenbank hinzufügen Methode
· @Caching
o Funktion:Mehrere auf eine Methode anzuwendende Cacheoperationen neu kombinieren
o Anwendung: kombinierte Verwendung der oben genannten Annotationen


Kommentarbereich

Wird gesendet

Design auswählen

Das Design gilt für die gesamte Website.