When the our spring-framework project is started, we defined multi CacheManager, but not set which is primary, then running the class AbstractApplicationContext.finishBeanFactoryInitialization, there is an IllegalStateException:
No CacheResolver specified, and no unique bean of type CacheManager found. Mark one as primary or declare a specific CacheManager to use.
But it not catched, it only catched BeansException, then it will do the finally code, it will lose some key message to help find the issue.
It's hard to find the issue because the exception is not logged. So can spring catch the exception of IllegalStateException or RuntimeException?
It's solved by setting the primary CacheManager via @Primary
.
Code:org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
There has exception code: org.springframework.cache.interceptor.CacheAspectSupport.afterSingletonsInstantiated:
@Override
public void afterSingletonsInstantiated() {
if (getCacheResolver() == null) {
// Lazily initialize cache resolver via default cache manager...
Assert.state(this.beanFactory != null, "CacheResolver or BeanFactory must be set on cache aspect");
try {
setCacheManager(this.beanFactory.getBean(CacheManager.class));
}
catch (NoUniqueBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no unique bean of type " +
"CacheManager found. Mark one as primary or declare a specific CacheManager to use.");
}
catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no bean of type CacheManager found. " +
"Register a CacheManager bean or remove the @EnableCaching annotation from your configuration.");
}
}
this.initialized = true;
}
Comment From: snicoll
The error message is intended to guide you as what the next steps are for this:
No CacheResolver specified, and no unique bean of type CacheManager found. Mark one as primary or declare a specific CacheManager to use.
I don't really understand how the nested cause would have helped you. If you have multiple cache managers, you need to flag one as @Primary
or you should define a CacheResolver
instead. Both these options are provided in the error message.
Comment From: Bluselye
My means that the IllegalStateException not catched, so my project is also running but not have error log or other message, and the project is also can't do request, so can in class AbstractApplicationContext's method: refresh() can catch the same as IllegalStateException?
Comment From: snicoll
I am not sure I understand what you mean by that. It must not be caught. It is supposed to fail hard and prevent your application to start as its configuration is incorrect.
Comment From: Bluselye
Yes,my application is incorrect, but the exception is not logged,so it's make me many hours to find the exception. If I can know the exception,I will quickly solve it.
Comment From: snicoll
I don't see how the exception would not be logged. Can you please share a small sample that demonstrates the problem (either a zip attached to this issue or a link to a GitHub repo)
Comment From: pmv
No CacheResolver specified, and no unique bean of type CacheManager found. Mark one as primary or declare a specific CacheManager to use.
I don't really understand how the nested cause would have helped you. If you have multiple cache managers, you need to flag one as @Primary or you should define a CacheResolver instead. Both these options are provided in the error message.
Both options are provided, but having the nested cause is clearer. Having
java.lang.IllegalStateException: No CacheResolver specified, and no unique bean of type CacheManager found. Mark one as primary or declare a specific CacheManager to use.
at org.springframework.cache.interceptor.CacheAspectSupport.afterSingletonsInstantiated(CacheAspectSupport.java:223)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:899)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
means I start in CacheAspectSupport.afterSingletonsInstantiated and drill down to the real problem. Whereas if I'd have known
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.cache.CacheManager' available: more than one 'primary' bean found among candidates: [...]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.determinePrimaryCandidate(DefaultListableBeanFactory.java:1574)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1171)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveBean(DefaultListableBeanFactory.java:419)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:349)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
at org.springframework.cache.interceptor.CacheAspectSupport.afterSingletonsInstantiated(CacheAspectSupport.java:220)
from the outset, it clarifies things a bit ("So that's why @Primary on the bean I want doesn't work")
"Hours" is definitely an exaggeration in my case (10 minutes?), but the missing root cause did feel like enough of a "bug" that I searched for and found this issue.
Comment From: snicoll
Your case is different, you have two beans marked as @Primary
. I think the bottom line here is " and no unique bean of type CacheManager found". I'll have a look to see if we can provide a bit more context.
Comment From: Bluselye
Thanks for take my advice, I think it's better than before.