skip to Main Content

In Spring Boot, I would like to use one cache manager defined as bean plus Redis cache defined in application.yaml. But after define cache manager like bean the other one in application.yaml is ignored.

 @Bean
public CacheManager cacheManager() {
    return new ConcurrentMapCacheManager(LDAP_CACHE_NAME) {

        @Override
        protected Cache createConcurrentMapCache(final String name) {
            return new ConcurrentMapCache(name, CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.SECONDS).maximumSize(100).build().asMap(), false);
        }
    };
}

application.yaml:

spring:
  redis:
    host: localhost
  cache:
    type: redis
    cache-names: REDISCACHE
    cache-specs:
      myCustomCache:
        time-to-live: 600s

2

Answers


  1. With Spring Boot, you do NOT need to explicitly define/declare a CacheManager bean.

    For example, when both the spring-boot-starter-cache (here) and spring-boot-starter-data-redis (here) dependencies are declared on your Spring Boot application classpath, then Spring Boot will auto-configure a CacheManager bean for you. Also, check out this link from start.spring.io, which will get you started.

    You still need to enable caching, by declaring the Spring cache @EnableCaching annotation on 1 of your Spring application @Configuration classes (see docs), but you do NOT need to explicitly declare a CacheManager bean. Doing so will actually override the auto-configuration in Spring Boot even, and in your case, that is this Spring Boot auto-configuration class (source) in particular (for Redis). See the Redis CacheManager bean here provided for you by Spring Boot.

    WARNING: If you have more than 1 caching provider on your Spring Boot application classpath at a time, then you will need to explicitly declare which provider you intend to use for caching. You can do this with the spring.cache.type property in Spring Boot application.properties, or application.yaml file. See the first "Tip" in the section of the docs.

    TIP: You can even customize the Spring Boot auto-configured, Redis-based CacheManager bean by declaring a bean of type RedisCacheManagerBuilderCustomizer (Javadoc, source).

    So, what you have effectively done by declaring an explicit CacheManager bean (the ConcurrentMapCacheManager bean, no less), is override Spring Boot’s auto-configuration (for instance, this). That is, Spring Boot will only auto-configure a Redis CacheManager bean if you have not explicitly done so yourself.

    This is what it means when Spring Boot says, "convention over configuration" unless you explicitly specify yourself, then Spring Boot quietly backs away and assumes you know what you are doing (See more details, here).

    To make matters worse, you declared a ConcurrentMapCacheManager bean, which will not use or store anything in Redis, as a caching provider. It will simply use a in-memory, ConcurrentMap (a java.util.concurrent.ConcurrentHashMap to be precise) to cache results from your Spring Boot application service methods, resulting in bypassing Redis altogether.

    So, you inadvertently shot yourself in the foot, 😉

    Hope this helps!

    Login or Signup to reply.
  2. From Grampa’s Trick Box

    @Component
    class MyAwesomeCMWrapper {
    
      final CacheManager redisCache;
      final CacheManager localCache; // or even more complex, e.g.: Map<String, CacheManager> chacheManagerMap;
      
      public MyAwesomeCMWrapper(@Autowired /*@Qualifier(..)*/ CacheManager redisCache) { // <- this is the auto-configured coming from spring 
        this.redisCache = redisCache;
        this.localCache = // init as you like, but *not* as a (direct) bean;)
        // ... new ConcurrentMapCacheManager(...); // <- this is your "local (no-bean) cache)
      }
    
      // getter...
    }
    

    The trick is:

    • not to use a @Bean CacheManager (tricking @ConditionalOnMissingBean(CacheManager.class)/leaving auto configuration)
    • but an "intermediate bean" (which offers acces to "local"/both caches)
    • redisCache is rather optional in this example, key point: "wrap your (autoconfigured + custom) bean"

    Then you can access the custom cache manager from spring context:

    • like #{@myAwesomeCMWrapper.localCache} (SpEL)
    • or @Autowried MyAwesomeCMWrapper ... getLocalCache() (java config)
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search