<cache:annotation-driven /> <context:component-scan base-package="org.bk.samples.cachexml"></context:component-scan> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <ref bean="defaultCache"/> </set> </property> </bean> <bean name="defaultCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"> <property name="name" value="default"/> </bean>
The factory bean ConcurrentMapCacheFactoryBean is a bean which is in turn responsible for creating a Cache bean.
My first attempt at translating this setup to a @Configuration style was the following:
@Bean public SimpleCacheManager cacheManager(){ SimpleCacheManager cacheManager = new SimpleCacheManager(); List<Cache> caches = new ArrayList<Cache>(); ConcurrentMapCacheFactoryBean cacheFactoryBean = new ConcurrentMapCacheFactoryBean(); cacheFactoryBean.setName("default"); caches.add(cacheFactoryBean.getObject()); cacheManager.setCaches(caches ); return cacheManager; }
This did not work however, the reason is that here I have bypassed some Spring bean lifecycle mechanisms altogether. It turns out that ConcurrentMapCacheFactoryBean also implements the InitializingBean interface and does a eager initialization of the cache in the "afterPropertiesSet" method of InitializingBean. Now by directly calling factoryBean.getObject() , I was completely bypassing the afterPropertiesSet method.
There are two possible solutions:
1. Define the FactoryBean the same way it is defined in the XML:
@Bean public SimpleCacheManager cacheManager(){ SimpleCacheManager cacheManager = new SimpleCacheManager(); List<Cache> caches = new ArrayList<Cache>(); caches.add(cacheBean().getObject()); cacheManager.setCaches(caches ); return cacheManager; } @Bean public ConcurrentMapCacheFactoryBean cacheBean(){ ConcurrentMapCacheFactoryBean cacheFactoryBean = new ConcurrentMapCacheFactoryBean(); cacheFactoryBean.setName("default"); return cacheFactoryBean; }In this case, there is an explicit FactoryBean being returned from a @Bean method, and Spring will take care of calling the lifecycle methods on this bean.
2. Replicate the behavior in the relevant lifecycle methods, in this specific instance I know that the FactoryBean instantiates the ConcurrentMapCache in the afterPropertiesSet method, I can replicate this behavior directly this way:
@Bean public SimpleCacheManager cacheManager(){ SimpleCacheManager cacheManager = new SimpleCacheManager(); List<Cache> caches = new ArrayList<Cache>(); caches.add(cacheBean()); cacheManager.setCaches(caches ); return cacheManager; } @Bean public Cache cacheBean(){ Cache cache = new ConcurrentMapCache("default"); return cache; }
Something to keep in mind when translating a FactoryBean from xml to @Configuration.
Note:
A working one page test as a gist is available here:
thanks for share :-)
ReplyDelete