首页 > 技术文章 > Spring源码(2)注入

mjoe 2021-07-12 11:53 原文

Spring采用IoC的思想,实现IoC思想的关键在于采用了注入的方式进行控制反转,在Spring中,注入有很多形式,这里只说明依赖注入方法注入自动注入三种。

1 依赖注入

根据官网介绍,依赖注入主要分为两种方式

  1. 构造函数注入
  2. Setter方法注入

1.1 构造函数注入

@Component
public class OneDao {

    public OneDao() {
        System.out.println("Dao无参构造函数已经调用");
    }

}
@Component
public class OneService {
    private OneDao oneDao;


    public OneService() {
        System.out.println("OneService无参构造函数已经被调用");
    }

    public OneService(OneDao oneDao) {
        System.out.println("OneService正在使用构造函数注入oneDao");
        this.oneDao = oneDao;
        System.out.println("OneService有参构造函数已经被调用");
    }

    public void test(){
        System.out.println(oneDao);
    }
}
@Configuration
public class Config {

    @Bean
    public OneDao oneDao(){
        OneDao oneDao = new OneDao();
        return oneDao;
    }

    @Bean
    public OneService oneService(){
        OneService oneService = new OneService(oneDao());
        return oneService;
    }
}
public class ComponentMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
        OneService oneService = (OneService) applicationContext.getBean("oneService");
        System.out.println(oneService);
    }
}

image-20210712111432322

1.2 setter注入

@Component
public class OneDao {

    public OneDao() {
        System.out.println("Dao无参构造函数已经调用");
    }

}
@Component
public class OneService {
    private OneDao oneDao;


    public OneService() {
        System.out.println("OneService无参构造函数已经被调用");
    }

    public void test(){
        System.out.println(oneDao);
    }

    @Autowired   //一定要加@Autowired ,否则默认不进行set注入
    public void setOneDao(OneDao oneDao) {
        System.out.println("正在使用setter进行注入OneDao");
        this.oneDao = oneDao;
    }
}
@Configuration
public class Config {

    @Bean
    public OneDao oneDao(){
        OneDao oneDao = new OneDao();
        return oneDao;
    }

    @Bean
    public OneService oneService(){
        OneService oneService = new OneService();
        return oneService;
    }
}
public class ComponentMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
        OneService oneService = (OneService) applicationContext.getBean("oneService");
        System.out.println(oneService);
    }
}

image-20210712111949128

tips:

@Autowired直接加到字段上跟加到set方法上有什么区别?为什么我们验证的时候需要将其添加到setter方法上?

  • 直接添加@Autowired注解到字段上,不需要提供setter方法也能完成注入。

1.3 同时采用构造注入加setter注入

构造注入在bean实例化阶段完成了,而后会调用setter注入,会对前面的构造注入进行覆盖。

2 方法注入

方法注入,是为了解决原型失效的问题。不管是setter注入还是构造函数注入,已经完成了该bean的依赖注入,后续不能进行依赖的修改。在使用该bean的时候,获得的还是依赖属性的单例,这样就失去了原型的作用。为了解决这个问题,出现了方法注入,在方法中对原型的bean进行重新获取和注入。

问题:

@Component
@Scope("prototype")
public class OneDao {

    public OneDao() {
        System.out.println("Dao无参构造函数已经调用");
    }

    int i;
    public void addAndPrint(int a){
        i = i + a;
        System.out.println(i);
    }

}
@Component
public class OneService {
    @Autowired
    private OneDao oneDao;


    public OneService() {
        System.out.println("OneService无参构造函数已经被调用");
    }

    public void test(int a){
        oneDao.addAndPrint(a);
    }

}
@Configuration
public class Config {

    @Bean
    public OneDao oneDao(){
        OneDao oneDao = new OneDao();
        return oneDao;
    }

    @Bean
    public OneService oneService(){
        OneService oneService = new OneService();
        return oneService;
    }
}
public class ComponentMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
        OneService oneService = (OneService) applicationContext.getBean("oneService");
        oneService.test(1);
        oneService.test(2);
        oneService.test(3);
    }
}

image-20210712112920660

问题:本来定义了OneDao类是一个原型的类,那么在OneService中应该获取的都是最新的OneDao对象,然后再调用其中的方法,预期结果应该是1,2,3,但是真是结果是1,3,6,这因为在OneService中已经完成OneDao的依赖注入,在测试的时候使用的始终是一个OneDao对象。

​ 解决这个问题,就可以通过方法注入的方式,每次都获取最新的OneDao对象即可。

  1. 通过注入applicationContext对象

    @Component
    public class OneService{
        @Autowired
        private ApplicationContext applicationContext;
    
        public OneService() {
            System.out.println("OneService无参构造函数已经被调用");
        }
    
        public void test(int a){
            OneDao oneDao = ((OneDao) applicationContext.getBean("oneDao"));
            oneDao.addAndPrint(a);
        }
    
    }
    
  2. 通过@LookUp的方式

@Component
public class OneService{
    public OneService() {
        System.out.println("OneService无参构造函数已经被调用");
    }

    public void test(int a){
        OneDao oneDao = lookUp();
        oneDao.addAndPrint(a);
    }

    @Lookup
    public OneDao lookUp(){
        return null;
    }
}

3 自动注入

自动注入和精确注入:所谓精确注入就是指通过构造函数或者setter方法指定了我们对象之间的依赖,也就是依赖注入,然后Spring根据我们指定的依赖关系,精确的给我们完成了注入。

​ 那么自动注入就是spring根据类型或者beanName自动帮我们进行注入。

注入模型:

官网给我们介绍了自动注入的四种模型,如图:

img

4 总结

img

推荐阅读