一 SpringCache に基づくキャッシュ方式
1.1 SpringCacheの概要
springcache はspring3.1でリリースされたもので、キャッシュの利用をカプセル化、抽象化しており、メソッドにannotationアノテーションを付けるだけでキャッシュされた結果を取得できる。まさにannotationを使っているため、業務コードとキャッシュコードの結合の問題を解決できる。つまり、業務コードに手を加えることなく、既存のコードをすぐにキャッシュに対応させ、開発者が意識せずキャッシュを利用できるようにしている。
1.2 SpringCacheの概要
キャッシュのフレームワークは数多くあり、それぞれに長所があります。たとえば Redis、Memcached、Guava、Caffeine などです。
私たちのプログラムでキャッシュを使おうとすると、これらのフレームワークと結合する必要があります。賢明なアーキテクトは、すでにインターフェースを利用して結合度を下げています。オブジェクト指向の抽象化とポリモーフィズムの特性を利用することで、ビジネスコードと具体的なフレームワークを分離しています。
ただし、コード内でキャッシュに関するインターフェースやメソッドを明示的に呼び出し、適切なタイミングでキャッシュにデータを格納し、適切なタイミングでキャッシュからデータを読み出す必要は依然としてあります。考えてみてください「AOP」の適用場面、これはまさにAOPに任せるべきことではないでしょうか?
Spring
3.1以降、[@Transactional](/Transactional )によるアノテーションベースのトランザクションと同様に、アノテーションによるCacheサポートとCache抽象化が提供されている。それ以前は一般にAOPで実装していた。
Spring Cacheを使う利点:
-
基本的なキャッシュの抽象化を提供し、下層の各種キャッシュを容易に切り替えられるようにする;
-
Cacheアノテーションを使えば、トランザクションと同じように、キャッシュのロジックを業務コードに透過的に適用でき、しかも少ないコードで実現できる;
-
トランザクションのロールバック時には、キャッシュも自動的にロールバックする機能を提供する;
-
比較的複雑なキャッシュロジックに対応できる;

1.3 Spring Cacheの中核設計
Spring 3.1から、Springのキャッシュ抽象化であるSpring Cacheが導入されました。定義することで org.springframework.cache.Cache と org.springframework.cache.CacheManager インターフェースによって異なるキャッシュ技術を統一し、JCacheアノテーションを使った開発の簡素化にも対応します。
Cacheインターフェースはキャッシュコンポーネントの仕様を定義し、キャッシュに関する各種操作を含みます。Springでは、Cacheインターフェース向けにさまざまな xxxCache の実装: RedisCache , EhCacheCache , ConcurrentMapCache など。
ソースコードの一部を通してCacheインターフェースと CacheManager インターフェース。
Cacheインターフェース
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(); } }
見てのとおり、Cache インターフェースはキャッシュの get put evict などの関連操作を抽象化しています。
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; } }
抽象クラス AbstractValueAdaptingCache Cacheインターフェースを実装しており、主にNULL値の処理ロジックを抽象化している。
-
allowNullValues属性はNULL値のキャッシュ処理を許可するかどうかを示す -
fromStoreValueメソッドがNULL値を処理する際のget操作を行い、プロパティallowNullValuesが true の場合、NullValue を NULL として処理する -
toStoreValueメソッドでNULL値を処理するput操作を行い、プロパティallowNullValuesが true の場合、NULL を NullValue として処理し、それ以外の場合は例外を投げる -
toValueWrapperメソッドが提供するCacheインターフェース内でgetメソッドのデフォルト実装で、キャッシュから値を読み取り、さらに次を通じてfromStoreValue変換し、最後に次の形式にラップするSimpleValueWrapper返す -
ValueWrapper get(Object key)とT get(Object key, @Nullable Classtype)メソッドは上述のメソッドに基づいて実装されています -
ValueWrapper get(Object key)と@Nullable Classtype)メソッドは上述のメソッドに基づいて実装されています -
lookup抽象メソッドは、サブクラスが実際のキャッシュ値を取得するために使われます
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 抽象クラスを継承した AbstractValueAdaptingCache は、Spring のデフォルトのキャッシュ実装である。キャッシュ対象オブジェクトのコピーを保存することに対応し、SerializationDelegate
serialization がシリアライズを処理する。デフォルトは null で、参照に基づくキャッシュとなる。キャッシュ関連の操作は基底クラスに基づく AbstractValueAdaptingCache の
null 値の扱いで、デフォルトでは null が許可されます。
CacheManager
public interface CacheManager { @Nullable //获取指定name的Cache,可能延迟创建 Cache getCache(String name); //获取当前CacheManager下的Cache name集合 Collection<String> getCacheNames(); }
CacheManager name に基づいて
Cache の集合を管理する。もちろん、 CacheManager にも多くの実装クラスがあります。例えば ConcurrentMapCacheManager 、 AbstractCacheManager および SimpleCacheManager 、これらの xxxCacheManager クラスはいずれもCacheの管理ルールを定めるためのものなので、ここではこれ以上掘り下げません。
二 SpringCacheの使用
2.1 使い方
Spring Cacheはまさにそのようなフレームワークである。AOPを利用してアノテーションに基づくキャッシュ機能を実現し、適切な抽象化も行っている。業務コードでは、下層でどのキャッシュフレームワークを使っているかを気にする必要はなく、アノテーションを一つ付けるだけでキャッシュ機能を実現できる。
依存関係を追加
<!--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の設定
Applicationの起動クラスに@EnableCachingアノテーションを追加する
@SpringBootApplication @MapperScan("com.xxxx.dao") @EnableCaching public class AxxxxApplication { public static void main(String[] args) { SpringApplication.run(AxxxxApplication.class, args); } }
cache のキャッシュタイプを設定する。ここでは主にローカルキャッシュを紹介する
//方式一: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以及其他的方式,这里主要介绍以上两种方式
インターフェースのキャッシュを実装する
//控制器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 SpringCacheアノテーション詳解
@Cacheableアノテーション
キャッシュにない場合:データベースを検索し、キャッシュに保存し、結果を返す、
キャッシュにある場合:結果をそのまま返す
| パラメーター | 説明 | 例 |
| value | キャッシュの名前。springの 設定ファイルで定義し、少なくとも一つ指定する必要があります |
例えば: @Cacheable(value=”mycache”) @Cacheable(value={”cache1”,”cache2”} |
| key | キャッシュのkey。空でも構いません。指定する場合は SpEL式で記述します。指定しない場合は、デフォルトでメソッドの全引数を組み合わせます | @Cacheable(value=”testcache”,key=”#userName”) |
| condition | キャッシュの条件。空でもよく、SpEL で記述し、 true または false を返す。true の場合にのみキャッシュする | @Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
役割:キャッシュへの書き込みに使用でき、結果をキャッシュに保存することで、以後の呼び出し時に実際のメソッドを再実行せず、キャッシュ内の値を直接返せるようになります。 最も簡単な使い方は、アノテーション名=キャッシュ名で、使用例は次のとおりです:
@Cacheable(value="accountCache")// 使用了一个缓存名叫 accountCache public Account getAccountByName(String userName) { // 方法内部实现不考虑缓存逻辑,直接实现业务 System.out.println("real query account."+userName); return getFromDB(userName); }
@CacheEvictアノテーション
@CacheEvict:キャッシュを削除するアノテーションであり、古いデータや不要なデータの削除に非常に役立つ。ここではパラメーター(allEntries)も追加されており、allEntries=trueに設定すると、すべてのエントリーを一括削除できる
| パラメーター | 説明 | 例 |
| value | キャッシュの名前。springの 設定ファイルで定義し、少なくとも一つ指定する必要があります | @CacheEvict(value=”my cache”) |
| key | キャッシュのkey。空でも構いません。指定する場合は SpEL式で記述します。指定しない場合は、デフォルトでメソッドの全引数を組み合わせます | @CacheEvict(value=”testcache”,key=”#userName”) |
| condition | キャッシュの条件。空でもよく、SpEL で記述し、 true または false を返す。true の場合にのみキャッシュする | @CacheEvict(value=”testcache”,condition=”#userName.length()>2”) |
| allEntries | すべてのキャッシュ内容を消去するかどうか。デフォルトは false。true を指定すると、メソッド呼び出し後にすべてのキャッシュが直ちに消去される | @CachEvict(value=”testcache”,allEntries=true) |
| beforeInvocation | メソッドの実行前にキャッシュをクリアするかどうか。デフォルトは falseで、trueに指定すると、メソッドがまだ実行されていない時点でキャッシュをクリアする。デフォルトでは、メソッドの実行で例外がスローされた場合、キャッシュはクリアされない | @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アノテーション
@CachePut:メソッドの実行に影響を与えずにキャッシュを更新したい場合、このアノテーションを使用できます。つまり、常にそのメソッドを実行し、その結果をキャッシュに格納します;
| パラメーター | 説明 | 例 |
| value | キャッシュの名前。springの 設定ファイルで定義し、少なくとも一つ指定する必要があります | @CachePut(value=”my cache”) |
| key | キャッシュのkey。空でも構いません。指定する場合は SpEL式で記述します。指定しない場合は、デフォルトでメソッドの全引数を組み合わせます | @CachePut(value=”testcache”,key=”#userName”) |
| condition | キャッシュの条件。空でもよく、SpEL で記述し、 true または false を返す。true の場合にのみキャッシュする | @CachePut(value=”testcache”,condition=”#userName.length()>2”) |
@CachePut アノテーションは、メソッドが実行されることを保証すると同時に、メソッドの戻り値もキャッシュに記録し、キャッシュとデータベースの同期更新を実現します。
@CachePut(value="accountCache",key="#account.getName()")// 更新accountCache 缓存 public Account updateAccount(Account account) { return updateDB(account); }
@Cachingアノテーション
キャッシュを使用する際には、更新と削除を同時に行う可能性があり、複数のアノテーションを同時に使用する場合がある.そして@Cachingを使えば実現できる
//添加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
すべての@Cacheable()にはvalue=“xxx”という属性があるが、メソッドが増えると、その都度書くのはかなり面倒だ。一度にまとめて宣言できれば手間が省ける。そこで用意されているのが@CacheConfigという設定で、@CacheConfigは キャッシュ名を共有できるクラスレベルのアノテーションだ。メソッドに別の名前を記述した場合は、引き続きメソッドに記述した名前が優先される。
@CacheConfig("books") public class BookRepositoryImpl implements BookRepository { @Cacheable public Book findBook(ISBN isbn) {...} }
条件付きキャッシュ
以下に、よく使われる条件付きキャッシュの例をいくつか示します
//@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)
カスタムキャッシュアノテーション
たとえば先ほどの@Cachingの組み合わせでは、メソッドに付くアノテーションによってコード全体が煩雑に見えてしまう。この場合、カスタムアノテーションを使って、これらのアノテーションを一つにまとめることができる。例:
@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 { }
これで、メソッドに以下のコードを付けるだけで済み、コード全体がすっきりします。
@UserSaveCache public User save(User user)
拡張
たとえばfindByUsernameの際は、username–>userだけを格納するのではなく、id—>userとemail—>userも一緒に格納すべきです;こうすれば、次回idで検索したときに、キャッシュに直接ヒットします
@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; }
実際、id—>user;username—->user;email—>user;については、よりよい方法は id—>user;username—>id;email—>id;として、user を一つだけ保存することかもしれません;例えば:
@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コンテキストデータ
Spring Cacheは、利用可能なSpELコンテキストデータをいくつか提供しています。以下の表はSpringの公式ドキュメントから直接抜粋したものです:
| 名称 | 場所 | 説明 | 例 |
| methodName | rootオブジェクト | 現在呼び出されているメソッド名 | root.methodName |
| method | rootオブジェクト | 現在呼び出されているメソッド | root.method.name |
| target | rootオブジェクト | 現在呼び出されている対象オブジェクト | root.target |
| targetClass | rootオブジェクト | 現在呼び出されているターゲットオブジェクトのクラス | root.targetClass |
| args | rootオブジェクト | 現在呼び出されているメソッドの引数リスト | root.args[0] |
| caches | rootオブジェクト | 現在のメソッド呼び出しで使用するキャッシュの一覧(例えば@Cacheable(value={“cache1”, “cache2”})では、キャッシュは二つ) | root.caches[0].name |
| 引数名 | 実行コンテキスト | 現在呼び出されているメソッドの引数です。例えばfindById(Long id)なら、#idで引数を取得できます | user.id |
| result | 実行コンテキスト | メソッド実行後の戻り値(メソッド実行後に判定する場合のみ有効。例えば‘unless'、'cache evict'のbeforeInvocation=false) | result |
@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)
まとめ
キャッシュの宣言用に、springのキャッシュ機能は一連のjavaアノテーションを提供しています:
· @Cacheable
o
機能:キャッシュへの書き込みをトリガーします。キャッシュにない場合はデータベースを検索し、キャッシュに保存して結果を返します。キャッシュにある場合は、そのまま結果を返します
o 用途:データベースを検索メソッド
· @CacheEvict
o
機能:キャッシュのクリアをトリガーする
o 用途:データベースの削除または変更メソッド
· @CachePut
o
機能:キャッシュへの書き込み(メソッドの実行には影響しません)。
o 用途:データベースに追加メソッド
· @キャッシュ処理
o
機能:メソッドに適用する複数のキャッシュ操作を再び組み合わせる
o 応用:上記のアノテーションを組み合わせて使用する