SpringSourceの記事を読んでたら、Spring 3.1で "A declarative, annotation-driven caching model"とか書いてあって、 比較的大量なアクセスを受け付けるアプリを作る事が多いので、ちょっとチェックしてみたいな、と。 って事でさっそくやってみます。 ■ pom.xmlをSpring3.1に書き換える
<org.springframework-version>3.0.6.RELEASE</org.springframework-version>
<org.springframework-version>3.1.0.RELEASE</org.springframework-version>
■ てか、そもそもどういうテクノロジ? SpringSourceのブログのSPRING 3.1 M1: CACHE ABSTRACTIONを読むと、 ↓みたいな感じで仕掛けておくと、
@Cacheable("books") public Book findBook(ISBN isbn) {...}
キャッシュの中に、そのisbn番号のBookオブジェクトが見つかったら、 メソッドの実行をスキップして、returnしますよ、と。 引数が複数ある場合はあーでこーでみたいなテクニックがあるようですが、この際なので省略しますw んで、中身はっていうと、サクっと使えるのは 1. encache(http://ehcache.org/)っていうQuartz(ジョブスケ)とかもやってるTerracottaのアレと、 2. JavaのConcurrentHashMapを使ったシンプルな感じのヤツらしいです。(Not Distributed) Gemfireがウンたらカンたらって書いてあったけど、具体的な事は書いてなかったす。。 JPAの2nd-level cacheとの違いはーとかも書いてあったけど、それ使った事にゃい… ■ Springの定義ファイル的には ↓のような名前空間があって、
xmlns:cache="http://www.springframework.org/schema/cache"
↓こんな感じでアノテーションでイケるように仕掛けて、
<cache:annotation-driven />
↓cacheManagerの定義入れておくとOK、、のはずが、、
<!-- generic cache manager --> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/> <bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="books"/> </set> </property> </bean>
org.springframework.cache.concurrent.ConcurrentCacheFactoryBean って書いてあったから、bean定義そうしたら、そんなクラスないとか言われて、 正しくはConcurrent"Map"CacheFactoryBeanだそうで。。知らんがな…w ■ 動かしてみる Controllerのコードの中に↓のようなログを仕込んで
@RequestMapping(value = "/hoge/search/{id}", method = RequestMethod.GET) public String searchById(@PathVariable int id, Locale locale, Model model) { ★ logger.info("Controller: hoge Search By ID"); ★ hoge hoge = hogeDao.findById(id); String hogeJsonStr = JSON.encode(hoge); model.addAttribute("hogeJson", hogeJsonStr ); return "hoge"; }
Daoのコードの中に↓のようなログを仕込んで
@Cacheable("hoge") public hoge findById(int hogeId) { ★ logger.info("Dao: hoge Find By ID"); ★ RowMapper mapper = new BeanPropertyRowMapper(hoge.class); return this.template.query(SELECT_BY_ID, mapper, hogeId).get(0); }
動かそうと思ったら、、、
org.springframework.aop.framework.AopConfigException: Cannot proxy target class because CGLIB2 is not available. Add CGLIB to the class path or specify proxy interfaces. at org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy(DefaultAopProxyFactory.java:67)
トホホ…。だりぃ。。。 ってことで↓をpom.xmlに突っ込んでやって、、
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
動かしたら、、、@Cachableが効いてるのでログ的には最初の一回しかDaoのメソッドは呼ばれませんよ、と。
INFO : com.shinodogg.HogeController - Controller: hoge Search By ID INFO : com.shinodogg.dao.HogeDao - Dao: hoge Find By ID INFO : com.shinodogg.HogeController - Controller: hoge Search By ID INFO : com.shinodogg.HogeController - Controller: hoge Search By ID INFO : com.shinodogg.HogeController - Controller: hoge Search By ID INFO : com.shinodogg.HogeController - Controller: hoge Search By ID
キャッシュのクリアとか、Expireどうすんだ?とか、JVMのメモリどんだけ食うんだ?とか、 考えなきゃいけないことはイロイロありそうですが、 永続化のところは何かとボトルネックになりがちなので、 こういうのでちょっとでも裏を楽させてあげるのは良いことかなぁと。