I am having trouble creating a Spring CacheManager. When I try to boot up, I get the an error creating bean message.
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@EnableCaching
@Configuration
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
log.trace("Creating cache manager.");
return new ConcurrentMapCacheManager("myCache");
}
}
I've put a debug marker on the log line, but I don't reach it. Something seems to be happening before we even get to the method.
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244)
at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$6(ClassBasedTestDescriptor.java:350)
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheManager' defined in class path resource [org/chuck/config/CacheConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cache.CacheManager]: Illegal arguments to factory method 'cacheManager'; args: ; nested exception is java.lang.IllegalArgumentException: object is not an instance of declaring class
Solution (thanks to Nigel Savage)
Don't name the class CacheConfig
For the spring class/bean scan, the namespace has a bunch of cache annotations[1].
Here they are creating their own config bean/class for caching CacheConfig
, which is a typical name to use and has the classname CacheConfig.
The issue here is that the Spring developers have an annotation[1] @CacheConfig which also has a class name CacheConfig.
From the error it appears that the Spring CDI context is attempting to proxy[2] the org.chuck.config.CacheConfig.class via a BeanFactory for the org.springframework.cache.CacheManager class which has factory methods that reference the org.springframework.cache.annotation.CacheConfig class(in this case an interface)
I think this could only happen if the factory is not using the fully qualified class name, the only evidence I could quickly find for this is a comment from this issue[3]
simple solution is just to rename their own config bean/class
[1] https://spring.io/guides/gs/caching/
[2] you should learn how CDI uses proxy classes and scope heuristics to create objects, the proxy is a subclass of the bean that is created at run-time, we can get a hint for this in the error message object is not an instance of declaring class
https://docs.spring.io/spring-javaconfig/docs/1.0.0.m3/reference/html/creating-bean-definitions.html
[3] https://github.com/spring-projects/spring-framework/issues/19229
"You could of course specify a custom BeanNameGenerator for your scan and use more unique bean names, e.g. the fully-qualified class name."