Spring Bean重复 - NoUniqueBeanDefinitionException : expected single matching bean but found 2

本文分享一则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时,发现了两个定义,不知道用哪个。并且告诉了我具体的两个类:xxxServicesubXxxService

排查服务代码,发现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")

其他思考

  1. 代码设计不太好,后来把两个bean改成了工厂模式,处理这种相似业务更恰当,扩展更便捷
  2. 使用名称修饰不同的bean,后面的代码维护也不是很友好。还是尽可能避免这种情况。

以上。
感谢您的阅读。

你可能感兴趣的:(#,└,Spring)