Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  • to get a list of all cached entries
  • loop through all of them and check for needed dates/users
  • if found suitable - return that
  • if not found - cache is not available, run the method.

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

like image 363
Oleg Avatar asked Jun 13 '16 07:06

Oleg


2 Answers

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.

like image 143
John Blum Avatar answered Sep 18 '22 00:09

John Blum


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.

like image 31
Louis Jacomet Avatar answered Sep 18 '22 00:09

Louis Jacomet