本文分享一则Spring
NoUniqueBeanDefinitionException: expected single matching bean but found 2
的排查案例。
问题处理
公司的一个Spring服务,启动时报错,log如下
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.xxx.service.XxxService’ available: expected single matching bean but found 2: xxxService,subXxxService
错误说的比较清楚:装载bean com.xxx.service.XxxService
时,发现了两个定义,不知道用哪个。并且告诉了我具体的两个类:xxxService
和subXxxService
。
排查服务代码,发现xxxService有个子类,这样通过xxxService类型去注入bean时,会找到父类和子类两个类、无法决定注入哪个:
// 服务1
@Component
public class XxxService {...}
// 服务2(继承服务1)
@Component
public class SubXxxService extends XxxService {...}
// 服务调用方
@Component
public class ServiceUser {
@Autowired
private XxxService service;
}
两个bean都在使用,那只能给两个类加下标识来区分了:
// 服务1
@Component("service.xxx")
public class XxxService {...}
// 服务2(继承服务1)
@Component("service.xxx.sub")
public class SubXxxService extends XxxService {...}
// 服务调用方
@Component
public class ServiceUser {
@Resource(name = "service.xxx")
private XxxService service;
}
问题解决。
总结
问题原因
除了继承这种情况,还有其他几种情况:
- 两个Bean实现了同个接口,依赖Bean时使用接口注入Bean(本文的问题其实是这种情况的一个特例)
- 不同包有同名的bean
- 错误配置了类扫描,如xml和@ComponentScan重复配置、注解本身重复配置,导致bean注入两次
解决思路
只保留一个
如果业务上永远只会用到一个实现,那么可以使用@Primary
将该实现标识为优先注入:
@Primary
@Component
但一般业务中这种情况不多。
通过其他标识区分bean
定义bean有两种方案
@Component @Qualifier("service.xxx")
@Component("service.xxx")
注入依赖也有两种方案:
- 使用
@Resource
注解@Resource(name = "service.xxx")
- 使用
@Qualifier
注解@Autowired @Qualifier("service.xxx")
其他思考
- 代码设计不太好,后来把两个bean改成了工厂模式,处理这种相似业务更恰当,扩展更便捷
- 使用名称修饰不同的bean,后面的代码维护也不是很友好。还是尽可能避免这种情况。
以上。
感谢您的阅读。