解决多类型冲突的三种方式(@Primary、@Qualifier注解)
目录标题知识预热@Autowired(先byType后byName)@Qualifier(byName)@Primary(byType)@Primary和@Qualifier注解的作用注入多个相同类型示例代码启动时报错注意点解决方式方式1 - 变量名称方式2 - @Primary方式3 - @Qualifier知识预热@Autowired(先byType后byName)实际项目中,@Autowir
目录标题
知识预热
@Autowired(先byType后byName)
实际项目中,@Autowired,
默认是byType注入
,当发现多个实现类的时候,@Autowired会根据byName的方式注入
,name默认就是根据变量名
来的。
下面的例子中,则是先根据 UserService.class(先通过类型),然后根据 userService的name(再通过名字):
@Autowired
private UserService userService;
@Qualifier(byName)
配合@Autowired使用。用于指定通过name匹配bean。
注入时的限定符,用来指定注入哪一个实现类。通过byName的方式实现。
该例显示将注入name为customService的实现类:
@Autowired
@Qualifier("customService")
private UserService userService;
@Primary(byType)
在spring 中使用注解,常使用@Autowired, 默认是根据类型Type来自动注入的(如果类型找不到,再通过name查找。name指的是变量名
)。但有些特殊情况,对同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种的情况下 @Primary 的作用就出来了。
注意:@Primary只会让一个bean生效。
使用@Autowired注解执行注入时,默认是byType,当Spring容器中存在多个相同类型的Bean实例时,@Primary注解可以添加在类上,指定优先注入的Bean实例。
@Bean
@Primary
public Employee johnEmployee() {
return new Employee("john");
}
@Primary和@Qualifier注解的作用
@Primary和@Qualifier注解,都是处理@Autowired注入时,发现多个相同类型的冲突
时,进行解决。
注入多个相同类型示例
代码
public interface Singer {
String sing(String lyrics);
}
有下面的两个实现类:
@Component // 加注解,让spring识别
public class MetalSinger implements Singer{
@Override
public String sing(String lyrics) {
return "I am singing with DIO voice: "+lyrics;
}
}
========================================
@Component
public class OperaSinger implements Singer {
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
或者继承MetalSinger:
@Component
public class OperaSinger extents MetalSinger {
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
下面就是注入上面的接口实现类:
@Component
public class SingerService {
private static final Logger logger = LoggerFactory.getLogger(SingerService.class);
@Autowired
private Singer singer;
public String sing(){
return singer.sing("song lyrics");
}
}
启动时报错
有趣的事情就会发生,当你项目启动的时候会发现一个错误的结果或异常:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [main.service.Singer] is defined: expected single matching bean but found 2: metalSinger,operaSinger
.
提示很明确了,spring 根据类型无法选择到底注入哪一个。
注意点
- Singer的两个实现类MetalSinger、OperaSinger在IOC容器中的beanName分别是
metalSinger
、operaSinger
; - 而我们使用Singer的时写的变量名字为
singer
; - 我们都知道@Autowired默认通过byType从IOC中寻找bean,然后找到了两个bean;
- 为了确认具体是哪个bean实例,然后再通过byName寻找(也就是变量名singer)发现没有;
- 因此报错。
解决方式
方式1 - 变量名称
根据👆🏻上面的【注意点】信息,我们可以通过在使用实例的时候变量名
即为beanName
来解决:
@Component // 加注解,让spring识别
public class MetalSinger implements Singer{
@Override
public String sing(String lyrics) {
return "I am singing with DIO voice: "+lyrics;
}
}
========================================
@Component
public class OperaSinger implements Singer {
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
或者继承MetalSinger:
@Component
public class OperaSinger extents MetalSinger {
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
下面就是注入上面的接口实现类:
@Component
public class SingerService {
private static final Logger logger = LoggerFactory.getLogger(SingerService.class);
// IOC中有个beanName = metaSinger的bean实例
@Autowired
private Singer metaSinger;
// IOC中有个beanName = operaSinger的bean实例
@Autowired
private Singer operaSinger;
public String sing1(){
return metaSinger.sing("song lyrics");
}
public String sing2(){
return operaSinger.sing("song tom");
}
}
方式2 - @Primary
将@Primary添加到目标bean中,此处以OperaSinger为例,则另一个实例MetalSinger不会生效:
@Component // 加注解,让spring识别
public class MetalSinger implements Singer{
@Override
public String sing(String lyrics) {
return "I am singing with DIO voice: "+lyrics;
}
}
========================================
@Primary
@Component
public class OperaSinger implements Singer {
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
或者继承MetalSinger:
@Primary
@Component
public class OperaSinger extents MetalSinger {
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
调用:
@Component
public class SingerService {
private static final Logger logger = LoggerFactory.getLogger(SingerService.class);
@Autowired
private Singer singer;
public String sing(){
return singer.sing("song lyrics");
}
}
如果代码改成这样,再次运行,结果如下:
“I am singing in Bocelli voice: song lyrics”, 用@Primary 告诉spring 在犹豫的时候优先选择哪一个具体的实现。
方式3 - @Qualifier
@Qualifier和上面的【方式1 - 变量名称】很像,区别在于我的beanName不再来自变量名
,而是来自@Qualifier中的value
(@Qualifier的参数名称必须为我们之前定义@Component注解的名称之一):
@Component // 加注解,让spring识别
public class MetalSinger implements Singer{
@Override
public String sing(String lyrics) {
return "I am singing with DIO voice: "+lyrics;
}
}
========================================
@Component
public class OperaSinger implements Singer {
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
或者继承MetalSinger:
@Component
public class OperaSinger extents MetalSinger {
@Override
public String sing(String lyrics) {
return "I am singing in Bocelli voice: "+lyrics;
}
}
调用:
@Component
public class SingerService {
private static final Logger logger = LoggerFactory.getLogger(SingerService.class);
@Autowired
@Qualifier("metaSinger")
private Singer singer;
public String sing(){
return singer.sing("song lyrics");
}
}
更多推荐
所有评论(0)