Search code examples
javaspringconfigurationehcache

Java based configuration for Ehcache based caching not working


I am using java annotation based configuration for initializing ehcache based caching, with Spring 3.1.

Here is the sample code...

@Configuration
@EnableCaching
public class EhcacheConfig implements CachingConfigurer {

    .....

    @Bean
    public CacheManager cacheManager() {
        .....
        EhCacheManagerFactoryBean bean = new EhCacheManagerFactoryBean();
        bean.setCacheManagerName(CACHE_MANAGER);
        bean.setShared(Boolean.TRUE);
        File file = new File(property + Constants.Slash + EHCACHE_XML);
        bean.setConfigLocation(new FileSystemResource(file));

        try {
            bean.afterPropertiesSet();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        EhCacheCacheManager cm = new EhCacheCacheManager();
        cm.setCacheManager(bean.getObject());
        return cm;
    }

    public KeyGenerator keyGenerator() {
        return new DefaultKeyGenerator();
    }
}

There is a valid ehcache.xml with 1 cache declared in it.

This is all the configuration that I have for initializing ehcache with Spring. There is no XML based initialization in the application.

At runtime, I have noticed that cacheManager() is initialized, as expected. After its successful execution, the code fails to complete the initialization by erring out in:

CachingInterceptor.afterPropertiesSet() ->

if (this.cacheManager == null) {
    throw new IllegalStateException("'cacheManager' is required");
}

I have done some investigation.

It appears that the problem occurs when CachingInterceptor is being initialized by ProxyCachingConfiguration.

ProxyCachingConfiguration is derived from AbstractCachingConfiguration.

AbstractCachingConfiguration has a method called:

@PostConstruct
protected void reconcileCacheManager()

This method is not invoked. Had it been invoked, the cacheManager instantiated in EhcacheConfig.cacheManger() would have been setup correctly for used by the CacheInterceptor.afterPropertiesSet().

I do not understand the reason why reconcileCacheManager() is not invoked before CacheInterceptor.afterPropertiesSet() is invoked.

Am I missing something? Can some one help me with the problem that I am facing?

Thank you.


Solution

  • First, you might consider extracting the initialization of the EhCacheManagerFactoryBean to its own @Bean method.

    This way you can simply instantiate, configure, and return the FactoryBean without having to invoke afterPropertiesSet() yourself. This ensures that the object is a properly-managed Spring bean and that it can receive any other callbacks in might register for (like DisposableBean#destroy()) in this particular case.

    Assuming that new @Bean method is named "ecmfb", you can simply call ecmfb().getObject() from within your cacheManager() method, and you'll be guaranteed at that point that the FactoryBean contract (i.e. afterPropertiesSet()) has been satisfied.

    Second, you might care to know that your @Bean methods can throw any exception you like. So for example if you did not choose to extract the FactoryBean as I suggest above, you could still simplify the situation by declaring a 'throws Exception' clause on your cacheManager @Bean method. This will save you the current try/catch noise.

    Finally, to address why the @PostConstruct method is not being called, let me ask how you're bootstrapping the Spring container. If you're working with AnnotationConfig(Web)ApplicationContext, the CommonAnnotationBeanPostProcessor should be registered by default. The same is true if you're using or . CABPP is responsible for the detection and processing of annotations like @PostConstruct, @PreDestroy and others. Please provide a bit more information about your bootstrapping approach and we'll go from there.