S2Cachingとは
S2Cachingは、(現在のところ)メソッドの呼び出し内容と戻り値をキャッシュする機能を提供するためのinterceptorです。 ehCacheをキャッシュ実装として使用しています。 S2Cachingを使うことにより、Dao等に容易にキャッシュ機能を実現することが出来ます。適用先のクラスやメソッドのソースコードに修正は不要です。 まったく変化しないデータベース上のマスタにアクセスするDaoが頻繁に呼び出されるような場合に絶大な効果を発揮します。
S2Cachingの利用条件
S2Cachingは以下の環境でのみテストされています。
- S2Containerは 2.4.34 でのみテストされています
- S2Container 2.3系でも動作します。customizerがパッケージに含まれるために依存しているだけです。
- ehCache 1.6.2でのみテストされています。2.1.0などほかのバージョンでも動作するかもしれません。
S2Cachingを適用するには以下の条件を満たす必要があります
- 適用先メソッドの戻り値の型が Serializable であること
- 適用先メソッドの引数の型すべてが Serializable であること
適用のステップ(Interceptor編)
- ehcache.xml を記述します。実行時にehCacheによって読み出されます
- S2Containerによってキャッシュマネージャおよびinterceptorが参照できるように お使いのdiconに設定します。(例)
<component name="cacheManager" class="net.sf.ehcache.CacheManager"> @net.sf.ehcache.CacheManager@getInstance() <destroyMethod name="shutdown" /> </component> <component class="org.seasar.caching.interceptor.CallCacheInterceptor" name="callCache"> <arg>cacheManager</arg> <arg>"cache-01"</arg> </component>
これで、callCacheという名前でinterceptorを参照できます
- 適用先のメソッドに適用します。これはTraceInterceptorなど他のものと同じです
適用のステップ(Customizer編)
S2Container 2.4からはSMART-Deployが使えます。S2Cachingでも、Customizerを提供しており、アノテーションのついたメソッドに対してキャッシュを適用することができます。
- ehcache.xmlの設定、およびInterceptorの定義は必要です。上記適用のステップ(Interceptor編)を参考にしながら、Interceptorの定義をしておいてください。
- 適用先のクラスのメソッドに、CallCacheアノテーションを付けます。componentNameには CallCacheInterceptorのコンポーネント名を記入します。
@CallCache(componentName="callCache") public String calculateSomething(String condition) { ....
- logicCustomizerなど、意図するカスタマイザに CallCacheCustomizer を設定します。
キャッシュのされ方
S2Cachingは、呼び出しをフックし、毎回必ずキャッシュマネージャにキャッシュエントリが存在するかどうかを問い合わせます。 キャッシュエントリが存在しない場合は、メソッドをそのままコール(invocation.proceed())し、エントリが存在する場合は呼び出しを行わずにキャッシュしていた戻り値を返却します。 適用先のメソッドを実際に呼び出した場合には、戻り値を取得した時点でキャッシュエントリに追加します。
ehCacheでは、「キー」と「値」のペアがキャッシュのエントリです。
ここで、S2Cachingでは「キー」として、以下の内容を使っています。
- 適用先オブジェクトのSystem.identityHashCode値
- 適用先オブジェクトのClassオブジェクト
- 適用先メソッドの名前
- 適用先メソッドの引数列の型
- 呼び出しに用いられた引数の値
System.identityHashCodeを使っているため、キャッシュストアは永続化できません。
S2Cachingは「値」すなわちキャッシュされた戻り値への参照を以下にように使います。
- 適用先メソッドが返却した値は、キャッシュエントリに追加されます
- この参照は、実際には呼び出し元には返却せず、シリアライズを用いてオブジェクトの複製を作成し、これを返却します (つまり、同質性はありますが、同一ではありません)
- キャッシュがヒットした場合は、「値」をシリアライズを用いてオブジェクトの複製を作成し、これを返却します
このことで、返却された値は破壊操作を行うことができます。Daoに適用する場合にはエンティティのBeanが関係すると思われますが、Serializableにすることは難しくはないと考えています。
プロダクトとしてのTODO
- Flyweightなオブジェクトは、シリアライズ複製を取らないようにする
- Stringとかそのまま返却してもいいはず
- 複製を取らないような振る舞いも利用者が選べるように機能を追加する
- ehCacheが提供する分散キャッシュの例を実現してみる
- キーの生成をTemplateMethod(abstract), Strategyなどの形でユーザー定義できるようにする