It looks like SimpleCacheManager has the highest precedence when being instantiated from CacheAutoConfiguration.
This behaviour causes the following issue:
I have an application that uses Caffeine as the cache implementation and declares two @Cacheable methods, pointing to two separate cache keys. The application starts up properly without deffining any Cache bean, as Caffeine supports dynamic cache creation.
At the moment I want to configure one of those caches, I provide a Cache bean for one of those keys. At this point, my application fails to start as the other key doesn't exist. investigating the issue, I came to realize that, at the moment there is a bean of type Cache in the context, the SimpleCacheManager instance is created instead of the caffeine one. That's happening cause SimpleCacheManager is annotated with @ConditionalOnBean(Cache.class) and seems to be evaluated first in the chain, preventing any specific cache manager bean from being created due to the @ConditionalOnMissingBean(CacheManager.class) part.
I know I can target a specific cache type by using the property spring.cache.type, but I believe the default manager should not be the generic one should the application import a caching library as, most-likely, the intention is to use such imported library.
Just a small code sample to reproduce:
@EnableCaching
@Configuration
public class CachedConfiguration {
public static final String CACHE_KEY = "cache1";
@Bean(name = CACHE_KEY)
public Cache cache1() {
return new CaffeineCache(CACHE_KEY,
Caffeine.newBuilder()
.expireAfterWrite(Duration.ofSeconds(5))
.ticker(Ticker.systemTicker())
.recordStats()
.build());
}
}
---
@Service
public class CachedService {
@Cacheable(CachedConfiguration.CACHE_KEY)
public String method1(final String param) {
return param+"blabla";
}
@Cacheable("iDontExist")
public String method2(final String param) {
return param+"bleble";
}
}
That application will not work as it is, because "iDontExist" doesn't exist. Without the cache1 bean, the application starts up fine.
Comment From: snicoll
I don't understand what you're expecting to happen. If you've define at least a Cache bean, then it's going to be used. I don't understand why you expect the regular behaviour of caffeeine to kick in in this case. Please review this section of the documentation.
If that doesn't help, I think you'll have to provide more details.
Comment From: rubasace
Maybe it was a misunderstanding on my side; The thing is that we are configuring the specific caches using a Cache bean rather than via properties. We do this cause we want to configure individual caches parameters (such as TTL) and providing beans seemed like the most intuitive approach; AFAIK, the alternatives are using multiple cache managers or providing a custom cache resolver, and both seemed way more complicated.
What I was expecting was that, if I'm using Caffeine, I could still configure each individual caffeine cache parameters by providing a bean. I can still achieve that, but I have to indicate that I want to use Caffeine explicitly via properties.
Comment From: snicoll
@rubasace the generic provider (as explained in the section I've referenced) is meant to wrap all cache beans in a generic implementation. At this point, you're not using caffeine, but a generic implementation that "simply" delegates to the underlying caches. The generic provider can also be used if you have multiple caching provider implementations and you want to wrap them up in a single manager.
It's not clear what you expect to happen when someone requests the iDontExist cache. If you're creating a bean for that it will work. If you want the cache manager to create caches on the fly of a certain type, you should not use the generic implementation.