在上一篇文章中我们讲解了控制反转IOC的细节,接下来,我们学习依赖注⼊DI的细节。
依赖注⼊是⼀个过程,是指IOC容器在创建Bean时, 去提供运⾏时所依赖的资源,⽽资源指的就是对象.
依赖注入是一个过程,是指IOC容器在创建Bean时,去提供运行时所依赖的资源,而这里所说的资源,本质上就是我们程序中需要使用的各种对象。简单来说,DI的核心逻辑就是:把IOC容器中管理的对象取出来,赋值到需要该对象的类的属性中
我们首先来看一下依赖注入的三种方法

依赖注入的三种方法

属性注入

我们以将 Service 类注⼊到 Controller 类中为例来演示:

public class HelloController {
    /*
    下面这种注入方式为属性注入
     */
    @Autowired
    private UserService us;
public void print(){
        System.out.println("do Controller");
        us.print();
    }
}


运行结果如上

构造方法注入

 /*
    构造方法注入:
     */
    private UserService us;
    public HelloController(UserService us) {
        this.us = us;
    }

此时我们的程序是可以正常运行的,但如果我们引入一个无参数的构造方法呢?

public HelloController() {
    }
    public HelloController(UserService us) {
        this.us = us;
    }


这个时候程序就报错了,原因是this.us并没有被赋值,这是因为默认的构造方法是无参的构造方法。

@Autowired来指定默认的构造方法

此时我们就可以通过@Autowired来指定默认的构造方法:
(我们再引入一个UserConfig类来演示)

private UserService us;
    private UserConfig userConfig;
    public HelloController() {
    }
    public HelloController(UserService us) {
        this.us = us;
    }
    @Autowired
    public HelloController(UserService us, UserConfig userConfig) {
        this.us = us;
        this.userConfig = userConfig;
    }
    public void print(){
        System.out.println("do Controller");
        us.print();
        userConfig.print();
    }

通过上述案例,我们可以得出一个结论:
如果只有一个构造方法(有参数的构造方法)的时候,@Autowired是可以省略的,当存在多个构造方法的时候,默认的构造方法是午餐的构造方法,当然我们也可以通过@Autowired来指定默认的构造方法。

Setter方法注入

    private UserService us;
    private UserConfig userConfig;
    @Autowired
    public void setUs(UserService us) {
        this.us = us;
    }
    @Autowired
    public void setUserConfig(UserConfig userConfig) {
        this.userConfig = userConfig;
    }

需要注意的是该方法也需要搭配@Autowired注解来使用,而且每个Setter方法前都要加上该注解。

当我们有多个对象的时候,应该如何处理呢?

下面我们就来讲一下这种情况:

  @Bean({"s3","s4"})
    public Student s1(){
        return new Student("zhangsan",18);
    }
    @Bean
    public String name(){
        return "lisi";
    }
    @Bean
    public Student s2(String name){
        return new Student(name,18);
    }
 @Autowired
    private Student s1;
    public void print(){
        System.out.println("do Service");
        System.out.println(s1);
    }

这个时候我们的运行结果的报错信息如下:

这表示找到了2个Student类型的对象,所以这个时候就不知道要获取哪一个对象了。
解决办法如下:

@Primary注解

@Primary
    @Bean({"s3","s4"})
    public Student s1(){
        return new Student("zhangsan",18);
    }


当我们给其中任意一个对象添加Primary注解之后,就会拿到这个对象,并打印信息。

@Qualtifier注解

    @Qualifier("s3")
    @Autowired
    private Student s1;

@Resource注解

    @Resource(name="s2")
    private Student s1;

@Qualtifier注解的另一种用法

 @Bean
    public String name(){
        return "lisi";
    }
    @Bean
    public String name2(){
        return "lisi2";
    }
    @Bean
    public Student s2( @Qualifier("name2") String n){
        return new Student(n,18);
    }


我们可以在参数中指定要获取哪个对象。

结语

至此,我们已经讲解了依赖注入的三种核心实现方法以及多实例场景下的解决方案。总结来说,属性注入适合简单场景下的快速使用;而当存在多个同一类型Bean时,可根据实际需求选择@Primary、@Qualifier或@Resource注解来解决冲突。