spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
73.76k stars 40.35k forks source link

Perform failure analysis when a unique CacheManager bean cannot be found #13348

Open iNikem opened 6 years ago

iNikem commented 6 years ago

My Spring Boot application failed today:

2018-06-04 08:38:16.310  INFO 21232 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-04 08:38:16.321 ERROR 21232 --- [           main] o.s.boot.SpringApplication               : Application run failed

java.lang.IllegalStateException: No CacheResolver specified, and no unique bean of type CacheManager found. Mark one as primary (or give it the name 'cacheManager') or declare a specific CacheManager to use, that serves as the default one.
    at org.springframework.cache.interceptor.CacheAspectSupport.afterSingletonsInstantiated(CacheAspectSupport.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:777)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:327)
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:139)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
    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:246)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.testcontainers.containers.FailureDetectingExternalResource$1.evaluate(FailureDetectingExternalResource.java:30)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

I kinda understand what happened, but a proper FailureAnalyzer which could tell me where those multiple CacheManagers were defined would be of great help :)

snicoll commented 6 years ago

As this is throwing an IllegalStateException, an extension of CacheManagerValidator in the auto-configuration looks probably the best route.

snicoll commented 5 years ago

Closing in favour of PR #13916

mbhave commented 5 years ago

Blocked on this issue.

snicoll commented 2 months ago

I believe that this issue can be unblocked as the referenced issue has been fixed in 6.2.0-M2. We've decided to re-throw the usual NoUniqueBeanDefinitionException with additional information. This leads a demo app to fail as follows:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.cache.CacheManager' available: no CacheResolver specified and expected a single CacheManager bean, but found 3: [firstCacheManager,secondCacheManager,thirdCacheManager] - mark one as primary or declare a specific CacheManager to use.
    at org.springframework.cache.interceptor.CacheAspectSupport.afterSingletonsInstantiated(CacheAspectSupport.java:279) ~[spring-context-6.2.0-SNAPSHOT.jar:6.2.0-SNAPSHOT]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1050) ~[spring-beans-6.2.0-SNAPSHOT.jar:6.2.0-SNAPSHOT]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:970) ~[spring-context-6.2.0-SNAPSHOT.jar:6.2.0-SNAPSHOT]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625) ~[spring-context-6.2.0-SNAPSHOT.jar:6.2.0-SNAPSHOT]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:755) ~[spring-boot-3.3.0-20240429.120045-407.jar:3.3.0-SNAPSHOT]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:457) ~[spring-boot-3.3.0-20240429.120045-407.jar:3.3.0-SNAPSHOT]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:335) ~[spring-boot-3.3.0-20240429.120045-407.jar:3.3.0-SNAPSHOT]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1364) ~[spring-boot-3.3.0-20240429.120045-407.jar:3.3.0-SNAPSHOT]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1353) ~[spring-boot-3.3.0-20240429.120045-407.jar:3.3.0-SNAPSHOT]
    at com.example.demo.DemoApplication.main(DemoApplication.java:15) ~[classes/:na]

Process finished with exit code 1

Looking at NoUniqueBeanDefinitionFailureAnalyzer it backs off if the description is null. It looks like that's the case here as it doesn't find a matching nested cause. In this case, the bean lookup attempt is done programmatically, so there isn't a UnsatisfiedDependencyException to provide the injection points. Perhaps this rule can be relaxed a bit to process NoUniqueBeanDefinitionException in a generic fashion?

wilkinsona commented 2 months ago

I guess we're still blocked until we've upgraded to Framework 6.2. I'll move this into 3.x though.