Ehcache 3のドキュメントを見ていて、新しいキーワードが増えているのに気付きまして。
何これ?という感じですが、要はCacheManagerに管理されないCacheのようです(まんま)。Ehcache 3で導入されたコンセプトの模様。
Ehcache 3では、CacheManagerとCacheManagerから取得できるCacheをAPIの中心として使いますが、こちらはUserManagedCacheというインターフェースを使用します。まあ、UserManagedCacheもCacheインターフェースを実装していますが…。
CacheManagerにより管理されるCacheはCacheManagerによりライフサイクルを管理されますが、UserManagedCacheの場合はUserManagedCacheに対してcloseを呼び出すことになります。
このあたりが、「UserManaged」なんでしょうねぇ…。
使いどころですが、ドキュメントを見ると
Ideas are: method local caches, thread local caches or any other place where the lifecycle of the cache is shorter than the application lifecycle.
http://www.ehcache.org/documentation/3.0/usermanaged.html#what-are-user-managed-caches-and-what-do-they-offer
メソッドローカルキャッシュ、スレッドローカルキャッシュ、その他のアプリケーションよりライフサイクルが短いCacheとして使えばいいのでは?という感じで書かれています。
制限事項(?)として挙がっているのは、こちら。
As there is no longer a cache manager offering up services, the main limitation of user managed caches is that the user has to configure all required services by hand.
http://www.ehcache.org/documentation/3.0/usermanaged.html#limitations
Of course, if you find yourself requiring plenty of services, maybe the cache manager is a better option!
CacheManagerが提供するようなサービスがないよ、という話。CacheManager単位で設定していたような機能は、書く場所がないですからね…。また後で出てきますが、CacheManagerがないので設定はAPIを使ってプログラムとして記述する必要があります。設定ファイルがないよ、と。
とりあえず、使ってみましょう。
準備
Maven依存関係は、このように定義。
<dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>3.0.0.m4</version> </dependency>
Ehcache 3、現在は3.0.0.m4です。まだまだ正式版が出てくるのは遠そうですね。
あとは、テストコードのためにJUnitとAssertJを加えておきます。
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.3.0</version> <scope>test</scope> </dependency>
テストコードでは、以下のimport文があることを前提にします。
import java.util.concurrent.TimeUnit; import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.CacheManagerBuilder; import org.ehcache.Ehcache; import org.ehcache.UserManagedCache; import org.ehcache.UserManagedCacheBuilder; import org.ehcache.config.CacheConfigurationBuilder; import org.ehcache.config.Eviction; import org.ehcache.config.ResourcePoolsBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.expiry.Duration; import org.ehcache.expiry.Expirations; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat;
あとは、ドキュメントに沿って進めてみましょう。
使ってみる
UserManagedCacheを使うには、UserManagedCacheBuilderでUserManagedCacheを構成します。その後、取得したUserManagedCacheの初期化、利用、終了という順で操作していきます。
というのをまとめると、こんな感じになります。
@Test public void simpleUsage() { UserManagedCache<String, Integer> cache = UserManagedCacheBuilder .newUserManagedCacheBuilder(String.class, Integer.class) .build(false); cache.init(); cache.put("key1", 1); assertThat(cache.get("key1")) .isEqualTo(1); cache.remove("key1"); assertThat(cache.get("key1")) .isNull(); cache.close(); }
UserManagedCacheを作成して
UserManagedCache<String, Integer> cache = UserManagedCacheBuilder .newUserManagedCacheBuilder(String.class, Integer.class) .build(false);
initして
cache.init();
使って
cache.put("key1", 1); assertThat(cache.get("key1")) .isEqualTo(1); cache.remove("key1"); assertThat(cache.get("key1")) .isNull();
close。
cache.close();
initを省略する
先ほどの例では、UserManagedCacheを使う前にinitを呼び出す必要がありました。
cache.init();
ですが、これが嫌だという場合は、UserManagedCacheBuilder#build時にtrueを渡すとinitされた状態のUserManagedCacheを取得することができます。
@Test public void lessInit() { UserManagedCache<String, Integer> cache = UserManagedCacheBuilder .newUserManagedCacheBuilder(String.class, Integer.class) .build(true); cache.put("key1", 1); assertThat(cache.get("key1")) .isEqualTo(1); cache.remove("key1"); assertThat(cache.get("key1")) .isNull(); cache.close(); }
UserManagedCacheとCacheとEhcache
UserManagedCacheインターフェースですが、Cacheインターフェースを拡張したインターフェースになります。
https://github.com/ehcache/ehcache3/blob/v3.0.0.m4/api/src/main/java/org/ehcache/UserManagedCache.java#L28
さらに、CacheおよびUserManagedCacheの実体は、ともにEhcacheクラスです。
https://github.com/ehcache/ehcache3/blob/v3.0.0.m4/core/src/main/java/org/ehcache/Ehcache.java#L84
UserManagedCacheと、CacheManagerから取得したCacheのClassクラスを確認してみます。
@Test public void userManagedCacheIsEhcache() { UserManagedCache<String, Integer> cache = UserManagedCacheBuilder .newUserManagedCacheBuilder(String.class, Integer.class) .build(true); assertThat(cache) .isExactlyInstanceOf(Ehcache.class); CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("myCache", CacheConfigurationBuilder.newCacheConfigurationBuilder() .buildConfig(String.class, Integer.class)) .build(true); Cache<String, Integer> normalCache = cacheManager.getCache("myCache", String.class, Integer.class); assertThat(normalCache) .isExactlyInstanceOf(Ehcache.class); assertThat(cache.getClass()) .isEqualTo(normalCache.getClass()) .isEqualTo(Ehcache.class); cacheManager.close(); cache.close(); }
というわけで、同じEhcacheクラスが実体であることがわかります。
このことから、ホントにCacheManagerで管理されていない「だけ」のCacheなんだなぁというのがわかりますね…。
なお、Ehcacheクラスは内部でJCache向けの実装体まで作成していたりするのでいろいろフルスペック(?)です。
https://github.com/ehcache/ehcache3/blob/v3.0.0.m4/core/src/main/java/org/ehcache/Ehcache.java#L156
こう見ると、先の使用例で上げられていたような、メソッドのローカルキャッシュとして使用…というのはちょっとオーバースペックな気もするのですが…。もうちょっと、長い生存期間のキャッシュとして使う方がいいような気がしないでもないです。
ExpireやEvictionを設定する
Cacheの実体がCacheManagerで作成された時と同じということもあり、その他の設定もできます。
例えば、Expire(有効期限)。
@Test public void configureExpiry() throws InterruptedException { UserManagedCache<String, Integer> cache = UserManagedCacheBuilder .newUserManagedCacheBuilder(String.class, Integer.class) .withExpiry(Expirations.timeToIdleExpiration(new Duration(3L, TimeUnit.SECONDS))) .build(true); cache.put("key1", 1); assertThat(cache.get("key1")) .isEqualTo(1); TimeUnit.SECONDS.sleep(5L); assertThat(cache.get("key1")) .isNull(); cache.close(); }
Eviction(指定した要素数などを超えた場合に、Cacheから追い出し)。
@Test public void configureEviction() throws InterruptedException { UserManagedCache<String, Integer> cache = UserManagedCacheBuilder .newUserManagedCacheBuilder(String.class, Integer.class) .prioritizeEviction(Eviction.Prioritizer.FIFO) .withResourcePools(ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(3L, EntryUnit.ENTRIES)) .build(true); cache.put("key1", 1); cache.put("key2", 2); cache.put("key3", 3); cache.put("key4", 4); cache.put("key5", 5); TimeUnit.SECONDS.sleep(1L); assertThat(cache.get("key1")) .isNull(); assertThat(cache.get("key2")) .isNull(); assertThat(cache.get("key3")) .isEqualTo(3); assertThat(cache.get("key4")) .isEqualTo(4); assertThat(cache.get("key5")) .isEqualTo(5); cache.close(); }
などなど。