javaspring-bootspring-cache

Unable to create CacheManager bean


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


Solution

  • 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."