Oleg
Oleg

Reputation: 3014

Spring Cache - Create custom CacheManager

I'm using Spring Boot and EhCache to develop a calendar application. I'm trying to cache the following method:

@Override
@Cacheable(value = "concerts")
public List<Event> getEvents(String eventsForUser, Date startDate, Date endDate) throws Exception {
    return fetchEventsFromTheServer(eventsForUser, startDate, endDate);
}

The challenge is I would like to manipulate returned cached result. For example, check if there is cache for given dates but for a different user and then return it instead (as long as both users meet certain criteria).

So, before returning a result I would like:

I think the best would be to create a custom Cache Manager which will do all the manipulation with cached concert and use default auto configured cache for all other methods, but I can't get my custom manager to work and didn't find any good example on how to implement a custom CacheManager.

Here is what I have:

Application.java:

@SpringBootApplication
@ComponentScan
@EnableCaching
@EnableScheduling
public class SpringBootWebApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootWebApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(applicationClass);
    }

    private static Class<SpringBootWebApplication> applicationClass = SpringBootWebApplication.class;


    @Bean(name = "eventsCacheManager")
    public EventsCacheManager eventsCacheManager() {

        return new EventsCacheManager();
    }

    @Primary
    @Bean(name = "cacheManager")
    public CacheManager cacheManager() {
        return new EhCacheCacheManager(ehCacheCacheManager().getObject());
    }

    @Bean
    public EhCacheManagerFactoryBean ehCacheCacheManager() {
        EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean();
        cmfb.setConfigLocation(new ClassPathResource("ehcache.xml"));
        cmfb.setShared(true);
        return cmfb;
    }


}

EventsCacheManager.java

@Component
public class EventsCacheManager implements CacheManager  {

    @Override
    public Cache getCache(String s) {
        return null;
    }

    @Override
    public Collection<String> getCacheNames() {
        return null;
    }
}

EventsCacheManager.java - how to implement it?

@Component
public class EventsCacheManager implements CacheManager  {

    @Override
    public Cache getCache(String s) {
        return null;
    }

    @Override
    public Collection<String> getCacheNames() {
        return null;
    }
}

I would really appreciate if someone can give me an example on how I should implement my custom CacheManager

Upvotes: 7

Views: 29848

Answers (2)

Louis Jacomet
Louis Jacomet

Reputation: 14500

From my understanding of your question, I believe you are looking at the problem the wrong way.

Instead of having to browse cache contents to extract derived information, you should insert into the cache the derived information at the time of loading the main data.

For example, when loading month based information, break it immediately into day based information and put that in a cache as well.

This description also should clearly indicate that what you want to do is beyond the capabilities of the Spring caching abstraction, as you need custom cache loading logic.

So I would not recommend hacking a CacheManager to hide that logic but instead do it from the data loading logic.

Upvotes: 1

John Blum
John Blum

Reputation: 7981

I did not spend much time thinking about your requirements/use case, but I do think a custom CacheManager would work in this situation, assuming the "custom" CacheManager logic is correct.

So, by default, Spring looks for a bean in the context with the name "cacheManager" and uses it for all cached operations. In your configuration, you clearly have 2 "CacheManagers" defined...

@Bean(name = "eventsCacheManager")
public EventsCacheManager eventsCacheManager() {

    return new EventsCacheManager();
}

@Primary
@Bean(name = "cacheManager")
public CacheManager cacheManager() {
    return new EhCacheCacheManager(ehCacheCacheManager().getObject());
}

I.e. the "eventsCacheManager" (custom) and "cacheManager" (standard). Indeed, you even marked the "cacheManager" as primary (using the @Primary annotation). Of course, had you not done that, Spring would certainly have complained since more than 1 bean of a given type (i.e. CacheManager) was found in the context when performing auto-wiring (which defaults to auto-wire by type).

So, in order to call out which cache management strategy (i.e. CacheManager) to use at runtime with a particular service call, you also need to tell Spring which CacheManager (aka strategy) to use, like so...

@Override
@Cacheable(value = "concerts", cacheManager="eventsCacheManager")
public List<Event> getEvents(String eventsForUser, Date startDate, Date endDate) throws Exception {
    return fetchEventsFromTheServer(eventsForUser, startDate, endDate);
}

I.e. using the cacheManager attribute in the @Cacheable annotation to indicate which caching strategy to use.

See Spring's Reference Doc on Custom cache resolution for more details.

Hope this helps get you going.

Upvotes: 2

Related Questions