宣言的なアノテーションベースのキャッシュ

キャッシュ宣言のために、Spring のキャッシュ抽象化は Java アノテーションのセットを提供します:

  • @Cacheable: キャッシュ作成をトリガーします。

  • @CacheEvict: キャッシュエビクションをトリガーします。

  • @CachePut: メソッドの実行を妨げることなくキャッシュを更新します。

  • @Caching: メソッドに適用される複数のキャッシュ操作を再グループ化します。

  • @CacheConfig: クラスレベルでいくつかの一般的なキャッシュ関連の設定を共有します。

@Cacheable アノテーション

名前が示すように、@Cacheable を使用して、キャッシュ可能なメソッド、つまり、結果がキャッシュに保存されるメソッドを区別できます。これにより、後続の呼び出し(同じ引数を使用)で、キャッシュ内の値は返されません。実際にメソッドを呼び出す必要があります。次の例に示すように、最も簡単な形式では、アノテーション宣言にはアノテーション付きメソッドに関連付けられたキャッシュの名前が必要です。

@Cacheable("books")
public Book findBook(ISBN isbn) {...}

上記のスニペットでは、findBook メソッドは books という名前のキャッシュに関連付けられています。メソッドが呼び出されるたびに、キャッシュがチェックされ、呼び出しがすでに実行されており、繰り返す必要がないかどうかが確認されます。ほとんどの場合、宣言されるキャッシュは 1 つだけですが、アノテーションでは複数の名前を指定できるため、複数のキャッシュが使用されます。この場合、メソッドを呼び出す前に各キャッシュがチェックされます。少なくとも 1 つのキャッシュがヒットした場合、関連する値が返されます。

キャッシュされたメソッドが実際に呼び出されなかった場合でも、値を含まない他のすべてのキャッシュも更新されます。

次の例では、複数のキャッシュを使用する findBook メソッドで @Cacheable を使用しています。

@Cacheable({"books", "isbns"})
public Book findBook(ISBN isbn) {...}

デフォルトのキー生成

キャッシュは基本的にキーと値のストアであるため、キャッシュされたメソッドの各呼び出しは、キャッシュアクセスに適したキーに変換する必要があります。キャッシングの抽象化では、次のアルゴリズムに基づいた単純な KeyGenerator を使用します。

  • パラメーターが指定されていない場合は、SimpleKey.EMPTY を返します。

  • パラメーターが 1 つしか指定されていない場合は、そのインスタンスを返します。

  • 複数のパラメーターが指定されている場合は、すべてのパラメーターを含む SimpleKey を返します。

パラメーターが自然キーを持ち、有効な hashCode() および equals() メソッドを実装している限り、このアプローチはほとんどのユースケースでうまく機能します。そうでない場合は、戦略を変更する必要があります。

別のデフォルトキージェネレーターを提供するには、org.springframework.cache.interceptor.KeyGenerator インターフェースを実装する必要があります。

デフォルトのキー生成戦略は、Spring 4.0 のリリースとともに変更されました。Spring の以前のバージョンでは、複数のキーパラメーターについて、equals() ではなくパラメーターの hashCode() のみを考慮するキー生成戦略が使用されていました。これにより、予期しないキーの衝突が発生する可能性があります (背景については spring-framework#14870 [GitHub] (英語) を参照してください)。新しい SimpleKeyGenerator は、このようなシナリオに複合キーを使用します。

以前のキー戦略を引き続き使用する場合は、非推奨の org.springframework.cache.interceptor.DefaultKeyGenerator クラスを構成するか、カスタムハッシュベースの KeyGenerator 実装を作成できます。

カスタムキー生成宣言

キャッシングは汎用的であるため、ターゲットメソッドには、キャッシュ構造の上に簡単にマッピングできないさまざまなシグネチャーが含まれている可能性が非常に高くなります。これは、ターゲットメソッドに複数の引数があり、そのうちいくつかのみがキャッシュに適している場合に明らかになります(残りはメソッドロジックによってのみ使用されます)。次の例を考えてみましょう。

@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

一見すると、2 つの boolean 引数は本の検索方法に影響を与えますが、キャッシュには使用できません。さらに、2 つのうち 1 つだけが重要で、もう 1 つは重要でない場合はどうでしょうか。

そのような場合、@Cacheable アノテーションを使用すると、key 属性を介してキーを生成する方法を指定できます。SpEL を使用して、目的の引数(またはネストされたプロパティ)を選択したり、操作を実行したり、コードを記述したりインターフェースを実装したりすることなく、任意のメソッドを呼び出すことができます。コードベースが大きくなるとメソッドのシグネチャーが大きく異なる傾向があるため、これはデフォルトのジェネレーターよりも推奨されるアプローチです。一部のメソッドではデフォルトの戦略が機能する場合がありますが、すべてのメソッドで機能することはほとんどありません。

次の例では、さまざまな SpEL 宣言を使用しています(SpEL に慣れていない場合は、自分の好みで Spring 式言語を参照してください)。

@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@Cacheable(cacheNames="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

上記のスニペットは、特定の引数、そのプロパティの 1 つ、任意の(静的)メソッドを選択するのがいかに簡単かを示しています。

キーの生成を担当するアルゴリズムが具体的すぎる場合、または共有する必要がある場合は、操作でカスタム keyGenerator を定義できます。これを行うには、次の例に示すように、使用する KeyGenerator Bean 実装の名前を指定します。

@Cacheable(cacheNames="books", keyGenerator="myKeyGenerator")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
key パラメーターと keyGenerator パラメーターは相互に排他的であり、両方を指定する操作は例外になります。

デフォルトのキャッシュ解決

キャッシング抽象化は、構成された CacheManager を使用して、操作レベルで定義されたキャッシュを取得する単純な CacheResolver を使用します。

別のデフォルトのキャッシュリゾルバーを提供するには、org.springframework.cache.interceptor.CacheResolver インターフェースを実装する必要があります。

カスタムキャッシュ解決

デフォルトのキャッシュ解決は、単一の CacheManager で動作し、複雑なキャッシュ解決の要件を持たないアプリケーションに適しています。

複数のキャッシュマネージャーで動作するアプリケーションの場合、次の例に示すように、各操作に使用する cacheManager を設定できます。

@Cacheable(cacheNames="books", cacheManager="anotherCacheManager") (1)
public Book findBook(ISBN isbn) {...}
1anotherCacheManager を指定します。

また、キー生成の置き換えと同様の方法で、CacheResolver を完全に置き換えることもできます。解決はすべてのキャッシュ操作に対してリクエストされ、実装が実行時引数に基づいて使用するキャッシュを実際に解決できるようにします。次の例は、CacheResolver を指定する方法を示しています。

@Cacheable(cacheResolver="runtimeCacheResolver") (1)
public Book findBook(ISBN isbn) {...}
1CacheResolver を指定します。

Spring 4.1 以降、キャッシュのアノテーションの value 属性は必須ではなくなりました。これは、アノテーションの内容に関係なく、CacheResolver がこの特定の情報を提供できるためです。

key および keyGenerator と同様に、cacheManager および cacheResolver パラメーターは相互に排他的であり、カスタム CacheManager は CacheResolver 実装によって無視されるため、両方を指定する操作は例外になります。これはおそらく期待するものではありません。

同期キャッシング

マルチスレッド環境では、特定の操作が同じ引数に対して(通常は起動時に)同時に呼び出される場合があります。デフォルトでは、キャッシュの抽象化は何もロックせず、同じ値が数回計算され、キャッシュの目的を無効にします。

これらの特定のケースでは、sync 属性を使用して、基になるキャッシュプロバイダーに、値の計算中にキャッシュエントリをロックするように指示できます。その結果、値の計算でビジー状態にあるスレッドは 1 つだけですが、他のスレッドはキャッシュ内のエントリが更新されるまでブロックされます。次の例は、sync 属性の使用方法を示しています。

@Cacheable(cacheNames="foos", sync=true) (1)
public Foo executeExpensiveOperation(String id) {...}
1sync 属性を使用します。
これはオプションの機能であり、お気に入りのキャッシュライブラリがサポートしていない場合があります。コアフレームワークによって提供されるすべての CacheManager 実装は、それをサポートします。詳細については、キャッシュプロバイダーのドキュメントを参照してください。

CompletableFuture およびリアクティブ戻り型を使用したキャッシュ

6.1 以降、キャッシュアノテーションは CompletableFuture とリアクティブな戻り値の型を考慮し、それに応じてキャッシュの対話を自動的に調整します。

CompletableFuture を返すメソッドの場合、その Future によって生成されたオブジェクトは完了するたびにキャッシュされ、キャッシュヒットのキャッシュルックアップは CompletableFuture 経由で取得されます。

@Cacheable("books")
public CompletableFuture<Book> findBook(ISBN isbn) {...}

Reactor Mono を返すメソッドの場合、その Reactive Streams パブリッシャーによって発行されたオブジェクトは、利用可能になるたびにキャッシュされ、キャッシュヒットのキャッシュルックアップは Mono ( CompletableFuture によってサポートされる) として取得されます。

@Cacheable("books")
public Mono<Book> findBook(ISBN isbn) {...}

Reactor の Flux を返すメソッドでは、その Reactive Streams パブリッシャーから発行された オブジェクトは List に集められ、そのリストが完了するたびにキャッシュされ、キャッシュにヒットした場合のキャッシュ検索は Flux として取得されます。(キャッシュされた List の値には CompletableFuture が付加されます):

@Cacheable("books")
public Flux<Book> findBooks(String author) {...}

このような CompletableFuture とリアクティブ適応は同期キャッシュにも機能し、同時キャッシュミスの場合に値を 1 回だけ計算します。

@Cacheable(cacheNames="foos", sync=true) (1)
public CompletableFuture<Foo> executeExpensiveOperation(String id) {...}
1sync 属性を使用します。
このような構成が実行時に機能するには、構成されたキャッシュが CompletableFuture ベースの取得が可能である必要があります。Spring が提供する ConcurrentMapCacheManager はその取得スタイルに自動的に適応し、CaffeineCacheManager は非同期キャッシュモードが有効になっている場合にそれをネイティブにサポートします (CaffeineCacheManager インスタンスに setAsyncCacheMode(true) を設定します)。
@Bean
CacheManager cacheManager() {
	CaffeineCacheManager cacheManager = new CaffeineCacheManager();
	cacheManager.setCacheSpecification(...);
	cacheManager.setAsyncCacheMode(true);
	return cacheManager;
}

最後に重要なことですが、アノテーション駆動型のキャッシュは、合成やバックプレッシャーを伴う高度なリアクティブな相互作用には適していないことに注意してください。特定のリアクティブメソッドで @Cacheable を宣言することを選択した場合は、Mono の場合は発行されたオブジェクト、または Flux の場合は事前に収集されたオブジェクトのリストを単に格納する、かなり粒度の粗いキャッシュインタラクションの影響を考慮してください。

条件付きキャッシュ

時々、メソッドは常にキャッシュするのに適していない場合があります(たとえば、指定された引数に依存する場合があります)。キャッシュアノテーションは、true または false のいずれかに評価される SpEL 式を取る condition パラメーターを介して、このようなユースケースをサポートします。true の場合、メソッドはキャッシュされます。そうでない場合は、メソッドがキャッシュされていないかのように動作します(つまり、キャッシュ内の値や使用されている引数に関係なく、メソッドは毎回呼び出されます)。例: 次のメソッドは、引数 name の長さが 32 より短い場合にのみキャッシュされます。

@Cacheable(cacheNames="book", condition="#name.length() < 32") (1)
public Book findBook(String name)
1@Cacheable に条件を設定します。

condition パラメーターに加えて、unless パラメーターを使用して、キャッシュへの値の追加を拒否できます。condition とは異なり、unless 式はメソッドが呼び出された後に評価されます。前の例をさらに詳しく説明するために、次の例のように、ペーパーバックの本だけをキャッシュしたい場合があります。

@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result.hardback") (1)
public Book findBook(String name)
1unless 属性を使用してハードバックをブロックします。

キャッシュの抽象化は、java.util.Optional の戻り型をサポートします。Optional 値が存在する場合、関連するキャッシュに保存されます。Optional 値が存在しない場合、null は関連するキャッシュに保存されます。#result は常にビジネスエンティティを参照し、サポートされているラッパーを参照しないため、前の例は次のように書き直すことができます。

@Cacheable(cacheNames="book", condition="#name.length() < 32", unless="#result?.hardback")
public Optional<Book> findBook(String name)

#result は、Optional<Book> ではなく Book を参照していることに注意してください。null である可能性があるため、SpEL のセーフナビゲーション演算子を使用します。

利用可能なキャッシング SpEL 評価コンテキスト

各 SpEL 式は、専用の context に対して評価されます。フレームワークは、組み込みパラメーターに加えて、引数名などの専用のキャッシュ関連メタデータを提供します。次の表は、キーおよび条件付き計算に使用できるように、コンテキストで使用できるようになっているアイテムを示しています。

表 1: SpEL 式で利用可能なキャッシュメタデータ
名前 ロケーション 説明 サンプル

methodName

ルートオブジェクト

呼び出されるメソッドの名前

#root.methodName

method

ルートオブジェクト

呼び出されるメソッド

#root.method.name

target

ルートオブジェクト

呼び出されるターゲットオブジェクト

#root.target

targetClass

ルートオブジェクト

呼び出されるターゲットのクラス

#root.targetClass

args

ルートオブジェクト

ターゲットを呼び出すために使用される引数(オブジェクト配列)

#root.args[0]

caches

ルートオブジェクト

現在のメソッドが実行されるキャッシュのコレクション

#root.caches[0].name

引数名

評価コンテキスト

特定のメソッド引数の名前。名前が使用できない場合 (たとえば、コードが -parameters フラグなしでコンパイルされた場合)、#a<#arg> 構文を使用して個々の引数も使用できます。ここで、<#arg> は引数インデックス (0 から始まる) を表します。

#iban or #a0 (you can also use #p0 or #p<#arg> notation as an alias).

result

評価コンテキスト

メソッド呼び出しの結果(キャッシュされる値)。unless 式、cache put 式(key を計算するため)、cache evict 式(beforeInvocation が false の場合)でのみ使用可能です。サポートされているラッパー(Optional など)の場合、#result はラッパーではなく実際のオブジェクトを参照します。

#result

@CachePut アノテーション

メソッドの実行を妨げずにキャッシュを更新する必要がある場合は、@CachePut アノテーションを使用できます。つまり、メソッドが常に呼び出され、その結果が(@CachePut オプションに従って)キャッシュに配置されます。@Cacheable と同じオプションをサポートし、メソッドフローの最適化ではなく、キャッシュの生成に使用する必要があります。次の例では、@CachePut アノテーションを使用しています。

@CachePut(cacheNames="book", key="#isbn")
public Book updateBook(ISBN isbn, BookDescriptor descriptor)
同じメソッドで @CachePut アノテーションと @Cacheable アノテーションを使用することは、動作が異なるため、一般的にはお勧めしません。後者では、キャッシュを使用してメソッドの呼び出しがスキップされますが、前者では、キャッシュの更新を実行するために呼び出しが強制されます。これは予期しない動作を引き起こし、特定のコーナーケース(相互に除外する条件を持つアノテーションなど)を除いて、そのような宣言は回避する必要があります。これらの条件は除外を確認するために事前に検証されるため、そのような条件は結果オブジェクト(つまり、#result 変数)に依存すべきではないことにも注意してください。

6.1 以降、@CachePut は CompletableFuture とリアクティブな戻り値の型を考慮し、生成されたオブジェクトが利用可能になるたびに put 操作を実行します。

@CacheEvict アノテーション

キャッシュの抽象化により、キャッシュストアの取り込みだけでなく、追い出しも可能になります。このプロセスは、古いデータや未使用のデータをキャッシュから削除できます。@Cacheable とは対照的に、@CacheEvict はキャッシュエビクションを実行するメソッド(つまり、キャッシュからデータを削除するトリガーとして機能するメソッド)を区別します。兄弟と同様に、@CacheEvict はアクションの影響を受ける 1 つ以上のキャッシュを指定する必要があり、カスタムキャッシュとキー解決または条件を指定でき、キャッシュ全体のエビクションが必要かどうかを示す追加パラメーター(allEntries)を備えています。エントリの追い出し(キーに基づく)ではなく、実行されます。次の例では、books キャッシュからすべてのエントリを削除します。

@CacheEvict(cacheNames="books", allEntries=true) (1)
public void loadBooks(InputStream batch)
1allEntries 属性を使用して、キャッシュからすべてのエントリを削除します。

このオプションは、キャッシュ領域全体をクリアする必要がある場合に便利です。前の例に示すように、各エントリを削除するのではなく(非効率的であるため時間がかかります)、すべてのエントリが 1 回の操作で削除されます。フレームワークは適用されないため、このシナリオで指定されたキーを無視します(1 つのエントリだけでなく、キャッシュ全体が削除されます)。

beforeInvocation 属性を使用して、メソッドが呼び出される前(デフォルト)または呼び出される前にエビクションを実行するかどうかを指定することもできます。前者は、残りのアノテーションと同じセマンティクスを提供します。メソッドが正常に完了すると、キャッシュに対するアクション(この場合はエビクション)が実行されます。メソッドが実行されない(キャッシュされる可能性がある)か、例外がスローされた場合、エビクションは発生しません。後者(beforeInvocation=true)では、メソッドが呼び出される前に常にエビクションが発生します。これは、エビクションをメソッドの結果に関連付ける必要がない場合に役立ちます。

void メソッドは @CacheEvict で使用できることに注意してください。メソッドはトリガーとして機能するため、戻り値は無視されます(キャッシュと相互作用しないため)。これは、キャッシュにデータを追加したり、キャッシュ内のデータを更新したりする @Cacheable の場合とは異なり、結果を必要とします。

6.1 以降、@CacheEvict は CompletableFuture とリアクティブな戻り値の型を考慮し、処理が完了するたびに呼び出し後の削除操作を実行します。

@Caching アノテーション

同じ型の複数のアノテーション(@CacheEvict や @CachePut など)を指定する必要がある場合があります。たとえば、条件やキー式がキャッシュごとに異なるためです。@Caching を使用すると、ネストされた複数の @Cacheable@CachePut@CacheEvict アノテーションを同じメソッドで使用できます。次の例では、2 つの @CacheEvict アノテーションを使用しています。

@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") })
public Book importBooks(String deposit, Date date)

@CacheConfig アノテーション

これまで、キャッシュ操作には多くのカスタマイズオプションが用意されており、操作ごとにこれらのオプションを設定できることがわかりました。ただし、カスタマイズオプションの一部は、クラスのすべての操作に適用する場合、構成が面倒な場合があります。たとえば、クラスのすべてのキャッシュ操作に使用するキャッシュの名前を指定することは、単一のクラスレベルの定義に置き換えることができます。これが @CacheConfig の出番です。次の例では、@CacheConfig を使用してキャッシュの名前を設定します。

@CacheConfig("books") (1)
public class BookRepositoryImpl implements BookRepository {

	@Cacheable
	public Book findBook(ISBN isbn) {...}
}
1@CacheConfig を使用してキャッシュの名前を設定します。

@CacheConfig は、キャッシュ名、カスタム KeyGenerator、カスタム CacheManager、カスタム CacheResolver の共有を可能にするクラスレベルのアノテーションです。このアノテーションをクラスに配置しても、キャッシュ操作は有効になりません。

操作レベルのカスタマイズは、常に @CacheConfig のカスタマイズセットをオーバーライドします。これにより、キャッシュ操作ごとに 3 つのレベルのカスタマイズが可能になります。

  • グローバルに構成されたもの、例: CachingConfigurer まで: 次のセクションを参照してください。

  • クラスレベルで、@CacheConfig を使用します。

  • 操作レベル。

プロバイダー固有の設定は、通常、CacheManager Bean(CaffeineCacheManager など)で利用できます。これらも実質的にグローバルです。

キャッシングアノテーションの有効化

キャッシュアノテーションを宣言してもアクションは自動的にトリガーされませんが、Spring の多くの機能と同様に、この機能は宣言的に有効にする必要があります(つまり、キャッシュが原因であると思われる場合は、削除して無効にすることができます)コード内のすべてのアノテーションではなく、1 行の構成行のみ)。

アノテーションのキャッシュを有効にするには、@EnableCaching を @Configuration クラスの 1 つに追加します。

@Configuration
@EnableCaching
class AppConfig {

	@Bean
	CacheManager cacheManager() {
		CaffeineCacheManager cacheManager = new CaffeineCacheManager();
		cacheManager.setCacheSpecification(...);
		return cacheManager;
	}
}

または、XML 構成の場合、cache:annotation-driven 要素を使用できます。

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:cache="http://www.springframework.org/schema/cache"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/cache https://www.springframework.org/schema/cache/spring-cache.xsd">

	<cache:annotation-driven/>

	<bean id="cacheManager" class="org.springframework.cache.caffeine.CaffeineCacheManager">
		<property name="cacheSpecification" value="..."/>
	</bean>
</beans>

cache:annotation-driven 要素と @EnableCaching アノテーションの両方を使用すると、AOP を介してキャッシュ動作をアプリケーションに追加する方法に影響を与えるさまざまなオプションを指定できます。構成は、@Transactional の構成と意図的に類似しています。

キャッシングアノテーションを処理するためのデフォルトのアドバイスモードは proxy です。これにより、プロキシのみを介した呼び出しのインターセプトが可能になります。同じクラス内のローカル呼び出しは、そのようにインターセプトすることはできません。インターセプトのより高度なモードについては、コンパイル時またはロード時のウィービングと組み合わせて aspectj モードに切り替えることを検討してください。
CachingConfigurer の実装に必要な高度なカスタマイズ(Java 構成を使用)の詳細については、javadoc を参照してください。
表 2: キャッシュアノテーション設定
XML 属性 アノテーション属性 デフォルト 説明

cache-manager

なし (CachingConfigurer javadoc を参照してください)

cacheManager

使用するキャッシュマネージャーの名前。デフォルトの CacheResolver は、このキャッシュマネージャーでバックグラウンドで初期化されます(設定されていない場合は cacheManager)。キャッシュ解決をよりきめ細かく管理するには、'cache-resolver' 属性の設定を検討してください。

cache-resolver

なし (CachingConfigurer javadoc を参照してください)

構成された cacheManager を使用する SimpleCacheResolver

バッキングキャッシュの解決に使用される CacheResolver の Bean 名。この属性は必須ではなく、'cache-manager' 属性の代替としてのみ指定する必要があります。

key-generator

なし (CachingConfigurer javadoc を参照してください)

SimpleKeyGenerator

使用するカスタムキージェネレーターの名前。

error-handler

なし (CachingConfigurer javadoc を参照してください)

SimpleCacheErrorHandler

使用するカスタムキャッシュエラーハンドラーの名前。デフォルトでは、キャッシュ関連の操作中にスローされた例外はすべてクライアントでスローされます。

mode

mode

proxy

デフォルトモード(proxy)は、Spring の AOP フレームワークを使用して、プロキシ化されるアノテーション付き Bean を処理します(前述のプロキシセマンティクスに従い、プロキシ経由で受信するメソッド呼び出しにのみ適用されます)。代わりに、代替モード(aspectj)は、影響を受けるクラスを Spring の AspectJ キャッシングアスペクトと織り、ターゲットクラスのバイトコードを変更して、あらゆる種類のメソッド呼び出しに適用します。AspectJ ウィービングでは、ロード時ウィービング(またはコンパイル時ウィービング)を有効にするだけでなく、クラスパスに spring-aspects.jar が必要です。(ロード時ウィービングを設定する方法の詳細については、Spring の構成を参照してください。)

proxy-target-class

proxyTargetClass

false

プロキシモードのみに適用されます。@Cacheable または @CacheEvict アノテーションが付けられたクラスに対して作成されるキャッシュプロキシの型を制御します。proxy-target-class 属性が true に設定されている場合、クラスベースのプロキシが作成されます。proxy-target-class が false の場合、または属性が省略された場合、標準の JDK インターフェースベースのプロキシが作成されます。(さまざまなプロキシ型の詳細な調査については、プロキシメカニズムを参照してください。)

order

order

Ordered.LOWEST_PRECEDENCE

@Cacheable または @CacheEvict アノテーションが付けられた Bean に適用されるキャッシュアドバイスの順序を定義します。(AOP アドバイスの順序付けに関連する規則の詳細については、アドバイスのオーダーを参照してください)順序付けが指定されていない場合、AOP サブシステムがアドバイスの順序を決定します。

<cache:annotation-driven/> は、それが定義されているのと同じアプリケーションコンテキスト内の Bean 上でのみ @Cacheable/@CachePut/@CacheEvict/@Caching を検索します。これは、DispatcherServlet の WebApplicationContext に <cache:annotation-driven/> を置くと、サービスではなくコントローラー内の Bean のみがチェックされることを意味します。詳細については、 "MVC" セクションを参照してください。
メソッドの可視性とキャッシュアノテーション

プロキシを使用する場合、キャッシュのアノテーションは、パブリック可視性を持つメソッドにのみ適用する必要があります。これらのアノテーションで protected メソッド、プライベートなメソッド、パッケージ private メソッドにアノテーションを付けた場合、エラーは発生しませんが、アノテーション付きメソッドは構成されたキャッシュ設定を示しません。バイトコード自体を変更するため、非 public メソッドにアノテーションを付ける必要がある場合は、AspectJ(このセクションの残りを参照)の使用を検討してください。

Spring は、インターフェースにアノテーションを付けるのではなく、@Cache* アノテーションで具象クラス(および具象クラスのメソッド)にのみアノテーションを付けることをお勧めします。確かに、@Cache* アノテーションをインターフェース(またはインターフェースメソッド)に配置できますが、これはプロキシモード(mode="proxy")を使用する場合にのみ機能します。ウィービングベースのアスペクト(mode="aspectj")を使用する場合、キャッシング設定は、ウィービングインフラストラクチャによるインターフェースレベルの宣言では認識されません。
プロキシモード(デフォルト)では、プロキシを介して受信する外部メソッド呼び出しのみがインターセプトされます。これは、呼び出されたメソッドが @Cacheable でマークされている場合でも、自己呼び出し(実際には、ターゲットオブジェクトの別のメソッドを呼び出すターゲットオブジェクト内のメソッド)が実行時に実際のキャッシュにつながらないことを意味します。この場合、aspectj モードの使用を検討してください。また、期待される動作を提供するためにプロキシを完全に初期化する必要があるため、初期化コード(つまり @PostConstruct)でこの機能に依存しないでください。

カスタムアノテーションの使用

カスタムアノテーションと AspectJ

この機能はプロキシベースのアプローチでのみ機能しますが、AspectJ を使用することで少し手間をかけて有効にできます。

spring-aspects モジュールは、標準のアノテーションのみのアスペクトを定義します。独自のアノテーションを定義した場合は、それらの側面も定義する必要があります。例については、AnnotationCacheAspect を確認してください。

キャッシングの抽象化により、独自のアノテーションを使用して、キャッシュの作成またはエビクションをトリガーするメソッドを識別できます。これは、キャッシュアノテーション宣言を複製する必要がないため、テンプレートメカニズムとして非常に便利です。これは、キーまたは条件が指定されている場合、またはコードベースで外部インポート(org.springframework)が許可されていない場合に特に役立ちます。残りのステレオタイプアノテーションと同様に、@Cacheable@CachePut@CacheEvict@CacheConfig をメタアノテーション(つまり、他のアノテーションにアノテーションを付けることができるアノテーション)として使用できます。次の例では、一般的な @Cacheable 宣言を独自のカスタムアノテーションに置き換えます。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Cacheable(cacheNames="books", key="#isbn")
public @interface SlowService {
}

前の例では、独自の SlowService アノテーションを定義しています。これには @Cacheable のアノテーションが付けられています。これで、次のコードを置き換えることができます。

@Cacheable(cacheNames="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

次の例は、前述のコードを置き換えることができるカスタムアノテーションを示しています。

@SlowService
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

@SlowService は Spring アノテーションではありませんが、コンテナーは実行時に自動的に宣言を取得し、その意味を理解します。前述のように、アノテーション駆動型の動作を有効にする必要があることに注意してください。