今回は、Spring Framework 4.3の変更点紹介シリーズの第3回で、キャッシュ関連の変更点を紹介します。
シリーズ
- 第1回:Spring 4.3 DIコンテナ関連の主な変更点
- 第2回:Spring 4.3 データアクセス関連の主な変更点
- 第4回:Spring 4.3 JMS関連の主な変更点
- 第5回:Spring 4.3 Web関連の主な変更点
- 第6回:Spring 4.3 WebSocket関連の主な変更点
- 第7回(最終回):Spring 4.3 テスト関連の主な変更点
動作検証環境
- Spring Framework 4.3.0.RELEASE
- Spring Boot 1.4.0.BUILD-SNAPSHOT (2016/6/11時点)
Caching Improvements
今回は、キャッシュ関連(@Cacheable
)の主な変更点をみていきます。
No | キャッシュ関連の主な変更点 |
---|---|
1 | マルチスレッド下での同時アクセス時に、キャッシュデータのロード処理を同期化できるようになります。 また、この変更に伴いCache インターフェースにget(Object key, Callable<T> valueLoader) メソッドが追加されています。 |
2 |
@Cacheable などキャッシュ制御用アノテーションのcondition 属性に指定するSpEL中で、DIコンテナに登録されているBeanを参照できるようになります。 |
3 |
ConcurrentMapCache で管理するオブジェクトをシリアライズできるようになります。 |
4 |
@Cacheable などのキャッシュ制御用アノテーションをメタアノテーションとして利用できるようになります。 |
キャッシュデータのロード処理を同期化できる
Spring 4.3から、@Cacheable
にsync
属性が追加され、true
を設定するとキャッシュデータのロード処理が同期化できるようになります。ここで注意しておきたいのは、sync
属性に指定した値はあくまで「ヒント」であり、実際に同期化するかはorg.springframework.cache.Cache
の実装クラスや利用するライブラリの実装に委ねられているということです。
ちなみに・・・Spring Frameworkが提供するConcurrentMap
ベースのCache
実装(ConcurrentMapCache
)は、Cache
オブジェクト単位でsynchronized
する実装になっています。なお、sync
属性のデフォルト値はfalse
(同期化しない)です。
なお、以降の説明では、キャッシュ実装としてConcurrentMapCache
を使用する前提で説明をします。ちなみに、@Cacheable
(アノテーション駆動のキャッシュ制御)を利用する場合は、最低限以下のBean定義が必要になります。
@EnableCaching // アノテーション駆動のキャッシュ制御を有効にする
@Configuration
public class CacheConfig {
@Bean
CacheManager cacheManager() {
return new ConcurrentMapCacheManager("configs"); // CacheManagerをBean定義する
}
}
データのロード処理を同期化しない(デフォルト)
同期化しない場合は、Spring 4.2と何もかわりません。
@Service
@CacheConfig(cacheNames = "configs")
public class ConfigService {
@Cacheable // sync属性を省略 or sync = false
public Config get(String key) {
// ...
}
}
複数スレッドから同時にアクセスがあった場合は、キャッシュキー(メソッドの引数)が一緒でも同時にメソッドが呼び出される可能性があります。なお、実際にキャッシュされるのは、メソッドが先に呼びだされたスレッドの返り値になります。
データのロード処理をを同期化する
Spring 4.3から追加された@Cacheable
のsync
属性をtrue
にするだけです。
@Cacheable(sync = true) // sync属性をtrueにする
public Config get(String key) {
// ...
}
キーに対応するオブジェクトがキャッシュされていない場合は、Cache
オブジェクト自体を同期化(synchronized
)してからデータをロードするメソッド(@Cacheable
メソッド)を呼び出します。
同期化する場合は、同期化の範囲を理解しておいた方がよいでしょう。ConcurrentMapCache
の実装では、同期化の範囲がキーではなく、Cache
オブジェクト自体になります。これをRDBMSに置き換えると、同期化の範囲が「行」ではなく「テーブル」になるイメージです。
なお、キャッシュデータのロード処理を同期化する場合は、以下がサポートされないので注意してください。
-
@Cacheable
のunless
属性の利用 - 複数キャッシュの指定
- 他のキャッシュ操作(
@CachePut
,@CacheEvict
など)との併用
@Cacheable
などのcondition
属性でBeanを参照ができる
Spring 4.3から、@Cacheable
, @CachePut
, @CacheEvict
のcondition属性に指定するSpEL中で、DIコンテナに登録されているBeanを参照できるようになります。 この改善により、キャッシュ操作の対象データを絞り込む条件をダイナミックに切り替えることができます。なお、SpELからBeanを参照する方法は「@EventListenerのcondition属性でBeanを参照ができる」をごらんください。
ConcurrentMapCache
で管理するオブジェクトをシリアライズできる
Spring 4.3から、ConcurrentMapCache
で管理するオブジェクトをシリアライズすることができるようになります。この改善により、キャッシュ内で管理しているオブジェクトと、キャッシュから返却するオブジェクトを別のインスタンスにすることができます。
たとえば、キャッシュから取得したオブジェクトの状態が変更されたとしても、キャッシュ内で管理しているオブジェクトの状態を変更したくない場合に有効な仕組みです。ただし、キャッシュからオブジェクトを取得する度に、byte配列をオブジェクトにするデシリアライズ処理が実行されるため、同じインスタンスを返却する方法に比べると、処理コストがかかることは意識しておきましょう。
キャッシュするオブジェクトをシリアライズする場合は、ConcurrentMapCacheManager
のstoreByValue
プロパティにtrue
を指定すればよいだけ・・・だと思ったのですが・・・、実際に動かしてみるとシリアライズされませんでした・・・ 。→ 【2016/5/31】 Spring JIRAのIssue(SPR-14314)にて修正されました
@EnableCaching
@Configuration
public class CacheConfig {
@Bean
CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager("configs");
cacheManager.setStoreByValue(true); // storeByValueプロパティをtrueにする
return cacheManager;
}
}
なお、この機能を使用する場合は、キャッシュするクラスにjava.io.Serializable
を実装しておく必要があります。
package com.example;
import java.io.Serializable;
public class Config implements Serializable { // Serializableを実装する
private String key;
private String value;
// ...
}
@Cacheable
などをメタアノテーションとして使用できる
Spring 4.3から、@Cacheable
, @CacheEvict
, @CachePut
, @Caching
をメタアノテーションとして利用できるようになります。
どういう切り口でカスタムアノテーションを作るかは自由ですが、ここでは、同期化が必要になるキャッシュを表現するカスタムアノテーションを使ってみます。同期化が必要になるのは・・・おそらく一貫性の担保が必要なキャッシュだと思うので、「@ConsistencyCacheable
」という名前にしてみます。
@Cacheable(sync = true) // メタアノテーションとして@Cacheableを指定
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ConsistencyCacheable {
}
@Cacheable(sync = true)
ではなく、作成したカスタムアノテーション(@ConsistencyCacheable
)を使用します。
@ConsistencyCacheable // カスタムアノテーションを指定
public Config get(String key) {
// ...
}
まとめ
今回は、キャッシュ関連の主な変更点を紹介しました。データのロード処理の同期化やオブジェクトのシリアライズ化のサポートなど、アプリケーションの安全性を高める仕組みが導入されたことで、エンタープライズアプリケーション開発での実用性が増した感じがします。次回は、「JMS関連の主な変更点」を紹介する予定です。
参考サイト
備考
オブジェクトのシリアラズ化の挙動
Spring 4.3からConcurrentMapCache
で管理するオブジェクトをシリアライズできるように改善されたが、単純にsotreByValue
プロパティにtrue
を指定しても機能が有効にならなかった・・・。バグの疑いがあるため、SPR-14314を作成しておいた。
↓
【2016/5/31】
修正されました。