知识预热

@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 根据类型无法选择到底注入哪一个。

注意点

  1. Singer的两个实现类MetalSinger、OperaSinger在IOC容器中的beanName分别是metalSingeroperaSinger
  2. 而我们使用Singer的时写的变量名字为singer
  3. 我们都知道@Autowired默认通过byType从IOC中寻找bean,然后找到了两个bean;
  4. 为了确认具体是哪个bean实例,然后再通过byName寻找(也就是变量名singer)发现没有;
  5. 因此报错。

解决方式

方式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");
    }
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐