深入Spring Boot:怎样排查expected single matching bean but found 2的异常
写在前面
这个demo来说明怎么排查一个常见的spring expected single matching bean but found 2的异常。
https://github.com/hengyunabc/spring-boot-inside/tree/master/demo-expected-single
调试排查 expected single matching bean but found 2 的错误
把工程导入IDE里,直接启动应用,抛出来的异常信息是:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found 2: h2DataSource1,h2DataSource2
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1041) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:345) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1090) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.init(DataSourceInitializer.java:71) ~[spring-boot-autoconfigure-1.4.7.RELEASE.jar:1.4.7.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_112]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_112]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_112]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_112]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:366) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:311) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:134) ~[spring-beans-4.3.9.RELEASE.jar:4.3.9.RELEASE]
... 30 common frames omitted
很多人碰到这种错误时,就乱配置一通,找不到下手的办法。其实耐心排查下,是很简单的。
抛出异常的原因
异常信息写得很清楚了,在spring context里需要注入/获取到一个DataSource bean,但是现在spring context里出现了两个,它们的名字是:h2DataSource1,h2DataSource2
那么有两个问题:
- 应用是在哪里要注入/获取到一个
DataSourcebean? - h2DataSource1,h2DataSource2 是在哪里定义的?
使用 Java Exception Breakpoint
在IDE里,新建一个断点,类型是Java Exception Breakpoint(如果不清楚怎么添加,可以搜索对应IDE的使用文档),异常类是上面抛出来的NoUniqueBeanDefinitionException。
当断点停住时,查看栈,可以很清楚地找到是在DataSourceInitializer.init() line: 71这里要获取DataSource:
Thread [main] (Suspended (exception NoUniqueBeanDefinitionException))
owns: ConcurrentHashMap<K,V> (id=49)
owns: Object (id=50)
DefaultListableBeanFactory.resolveNamedBean(Class<T>, Object...) line: 1041
DefaultListableBeanFactory.getBean(Class<T>, Object...) line: 345
DefaultListableBeanFactory.getBean(Class<T>) line: 340
AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).getBean(Class<T>) line: 1090
DataSourceInitializer.init() line: 71
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
Method.invoke(Object, Object...) line: 498
InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(Object) line: 366
InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(Object, String) line: 311
CommonAnnotationBeanPostProcessor(InitDestroyAnnotationBeanPostProcessor).postProcessBeforeInitialization(Object, String) line: 134
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).applyBeanPostProcessorsBeforeInitialization(Object, String) line: 409
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).initializeBean(String, Object, RootBeanDefinition) line: 1620
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 555
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 483
AbstractBeanFactory$1.getObject() line: 306
DefaultListableBeanFactory(DefaultSingletonBeanRegistry).getSingleton(String, ObjectFactory<?>) line: 230
DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 302
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String, Class<T>, Object...) line: 220
DefaultListableBeanFactory.resolveNamedBean(Class<T>, Object...) line: 1018
DefaultListableBeanFactory.getBean(Class<T>, Object...) line: 345
DefaultListableBeanFactory.getBean(Class<T>) line: 340
DataSourceInitializerPostProcessor.postProcessAfterInitialization(Object, String) line: 62
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).applyBeanPostProcessorsAfterInitialization(Object, String) line: 423
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).initializeBean(String, Object, RootBeanDefinition) line: 1633
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 555
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 483
AbstractBeanFactory$1.getObject() line: 306
DefaultListableBeanFactory(DefaultSingletonBeanRegistry).getSingleton(String, ObjectFactory<?>) line: 230
DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 302
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 197
DefaultListableBeanFactory.preInstantiateSingletons() line: 761
AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).finishBeanFactoryInitialization(ConfigurableListableBeanFactory) line: 867
AnnotationConfigEmbeddedWebApplicationContext(AbstractApplicationContext).refresh() line: 543
AnnotationConfigEmbeddedWebApplicationContext(EmbeddedWebApplicationContext).refresh() line: 122
SpringApplication.refresh(ApplicationContext) line: 762
SpringApplication.refreshContext(ConfigurableApplicationContext) line: 372
SpringApplication.run(String...) line: 316
SpringApplication.run(Object[], String[]) line: 1187
SpringApplication.run(Object, String...) line: 1176
DemoExpectedSingleApplication.main(String[]) line: 17
定位哪里要注入/使用DataSource
要获取DataSource具体的代码是:
//org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.init()
@PostConstruct
public void init() {
if (!this.properties.isInitialize()) {
logger.debug("Initialization disabled (not running DDL scripts)");
return;
}
if (this.applicationContext.getBeanNamesForType(DataSource.class, false,
false).length > 0) {
this.dataSource = this.applicationContext.getBean(DataSource.class);
}
if (this.dataSource == null) {
logger.debug("No DataSource found so not initializing");
return;
}
runSchemaScripts();
}
this.applicationContext.getBean(DataSource.class); 要求spring context里只有一个DataSource的bean,但是应用里有两个,所以抛出了NoUniqueBeanDefinitionException。
从BeanDefinition获取bean具体定义的代码
我们再来看 h2DataSource1,h2DataSource2 是在哪里定义的?
上面进程断在了DefaultListableBeanFactory.resolveNamedBean(Class<T>, Object...) 函数里的 throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet()); 这一行。
那么我们在这里执行一下(如果不清楚,先搜索下IDE怎么在断点情况下执行代码):
this.getBeanDefinition("h2DataSource1")
- 1
返回的信息是:
Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=demoExpectedSingleApplication; factoryMethodName=h2DataSource1; initMethodName=null; destroyMethodName=(inferred);
defined in com.example.demo.expected.single.DemoExpectedSingleApplication
可以很清楚地定位到h2DataSource1这个bean是在 com.example.demo.expected.single.DemoExpectedSingleApplication里定义的。
所以上面两个问题的答案是:
- 是spring boot代码里的
DataSourceInitializer.init() line: 71这里要获取DataSource,并且只允许有一个DataSource实例 - h2DataSource1,h2DataSource2 是在
com.example.demo.expected.single.DemoExpectedSingleApplication里定义的
解决问题
上面排查到的原因是:应用定义了两个DataSource实例,但是spring boot却要求只有一个。那么有两种办法来解决:
- 使用
@Primary来指定一个优先使用的DataSource,这样子spring boot里自动初始的代码会获取到@Primary的bean - 把spring boot自动初始化
DataSource相关的代码禁止掉,应用自己来控制所有的DataSource相关的bean
禁止的办法有两种:
在main函数上配置exclude
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class })在application.properties里配置:
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
总结
- 排查spring初始化问题时,灵活使用Java Exception Breakpoint
- 从异常栈上,可以很容易找到哪里要注入/使用bean
- 从
BeanDefinition可以找到bean是在哪里定义的(哪个Configuration类/xml)
深入Spring Boot:怎样排查expected single matching bean but found 2的异常的更多相关文章
- Spring Boot 自定义配置文件异常"expected single matching bean but found 2"
运行环境:Spring Boot 2.5.0, IDEA 2020.3.2 异常详细信息: Injection of resource dependencies failed; nested exce ...
- spring " expected single matching bean but found 2" 问题一例。
初入java,使用spring时遇到一个问题,左边是一个接口和实现.右边是service和实现. @Service@Transactional(rollbackFor = Exception.clas ...
- spring依赖注入单元测试:expected single matching bean but found 2
异常信息:org.springframework.beans.factory.UnsatisfiedDependencyException: Caused by: org.springframewor ...
- Spring 3.2 @Autowired异常:expected single matching bean but found 2
在使用Sping做单元测试时候,对RequestMappingHandlerAdapter(从处理器包装过来的适配器)进行自动装配, 发现报:expected single matching bean ...
- 多个@bean无法通过@resource注入对应的bean(org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found )
一.异常 org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ' ...
- expected single matching bean but found 2
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'acc ...
- 多数据源报错 expected single matching bean but found 2: xxx,xxx
问题: expected single matching bean but found 2: xxx,xxx 原因:在 Spring 容器中配置了两个类型Bean,Spring 容器将无法确定到底要用 ...
- MyBatis Plus:No qualifying bean of type 'com.baomidou.mybatisplus.mapper.BaseMapper<?>' available: expected single matching bean but found 4
场景: 应用MyBatis Plus 和通用Mapper 继承自ServiceImpl实现对Service里的方法进行包装再处理. public interface IServiceBase2< ...
- expected single matching bean but found 2: menusServiceImpl,IMenusService
问题如下: 接口也作为匹配的bean? 有点迷惑了....... 经过在网上找资料,发现和@MapperScan这个注解有关系,具体源码不止.但是这个注解会扫描路径下的所有类. 去掉这个注解就可以正常 ...
随机推荐
- 在Ubuntu16.04下安装 labelImg
首先按照 http://www.linuxdiyf.com/linux/13934.html 和 http://www.linuxdiyf.com/linux/13934.html 在ubuntu ...
- nginx 端口转发配置
nginx.conf #user nobody; worker_processes ; #error_log logs/error.log; #error_log logs/error.log not ...
- 一个服务器的IIS只能绑定一个HTTPS也就是443端口
默认情况一个服务器的IIS只能绑定一个HTTPS也就是443端口 要实现多个站点对应HTTPS只能更改IIS配置 地址:C:\Windows\System32\inetsrv\config\appli ...
- 用树莓派开Wifi热点
安装软件 首先设置软件源: vim /etc/apt/sources.list 查看软件源后面的版本,如果是wheezy,需要换成jessie wheezy是基于deb 7的版本 而现在是基于jess ...
- 轻型DNS服务器dnsmasq
源码安装 源码下载地址 apt 安装 apt install dnsmasq 编辑配置 vim /etc/dnsmasq.conf resolv-file=/etc/resolv.dnsmasq.co ...
- UA池和代理池在scrapy中的应用
一.下载中间件 下载中间件(Downloader Middlewares) 位于scrapy引擎和下载器之间的一层组件. - 作用: (1)引擎将请求传递给下载器过程中, 下载中间件可以对请求进行一系 ...
- CS229 1 .线性回归与特征归一化(feature scaling)
线性回归是一种回归分析技术,回归分析本质上就是一个函数估计的问题(函数估计包括参数估计和非参数估计),就是找出因变量和自变量之间的因果关系.回归分析的因变量是应该是连续变量,若因变量为离散变量,则问题 ...
- ThinkPHP5调用PHPExcel类实现导入导出
注意:extend是放置第三方类的地方,不要乱配置命名空间那些,引起不必要的类错误 代码如下 <?php namespace app\index\controller; use think\Co ...
- web api 本地测试
[最简单的,本人小白,大神勿喷] 一:创建web API 服务端 ①创建web api 的项目 ②在这个api项目的Web.config中加上如下几段话: <httpProtocol>&l ...
- HDU1848 Fibonacci again and again 博弈 SG函数
题意:三堆石子,每次能拿走斐波那契数个石子,先取完石子胜,问先手胜还是后手胜 石子个数<=1000 多组数据 题目链接:http://acm.hdu.edu.cn/showproblem.ph ...