聊透Spring依赖注入

本文详细探讨了Spring的依赖注入,尤其是@AutoWired和@Resource注解的使用。从依赖注入模型到自动注入的实现,解释了如何通过类型和名称查找注入对象,以及如何控制列表注入顺序和选择注入的子类。同时,文章对比了@AutoWired和@Resource的差异,包括它们的查找逻辑和应用场景。
摘要由CSDN通过智能技术生成

 依赖注入是我们使用Spring时最最常用的功能,甚至都不是之一。然而即使面对如此常用的功能,有时间难免也会力不从心,被它小小欺负一下,比如NoSuchBeanDefinitionExceptionNoUniqueBeanDefinitionExceptionUnsatisfiedDependencyException等等小问题总是时不时叨扰力一下,给原来快乐划水的日子平添一丝烦恼。

 究其原因,还是对Spring依赖注入的原理和实现不清楚所致,小伙伴们可以先自查一下这几个问题:

  1. @AutoWire注入时,是根据类型查找的,有多个实现的子类,是如何选择注入对象的,属性名称对注入对象的选择有影响吗?怎么控制注入特定子类呢?
  2. @AutoWire注入列表属性时,想控制列表顺序该如何实现呢?
  3. @Resource注入时,是根据名称查找的,如果名称查找不到,会根据类型查找吗?配不配置name属性,对查找逻辑有影响吗?
  4. Spring依赖注入的查找逻辑是怎么样的,是遍历属性和方法,然后通过反射注入的吗?

 如果小伙伴对Spring比较了解,对上述问题都了然指掌的话,请先受小弟一拜,大神非你莫属。如若不然,那我们就一起慢慢解开Spring依赖注入的神秘面纱,寻找上述问题的答案吧。

1. 依赖自动注入

 本章节我们先讨论一下Spring的依赖自动注入类型,虽然自动注入平时我们使用的并不多,但是在Spring内部和一些特定场景下,使用起来还是很方便的,我们简单了解一下。

1.1 依赖注入模型

 首先我们来看一下属性的自动注入类型有哪些,Spring默认的注入模型是AUTOWIRE_NO,这点我们通过 AbstractBeanDefinition的属性private int autowireMode = AUTOWIRE_NO可以知道,也就是不自动注入。除了AUTOWIRE_NO,Spring还提供了AUTOWIRE_BY_NAME(通过名称注入)、AUTOWIRE_BY_TYPE(通过类型注入)、AUTOWIRE_CONSTRUCTOR(通过构造器注入)等多种自动注入模型。

 不过我们使用最多的@AutoWired属性注入,却不属于自动注入模型的范围,因为它需要我们手动增加@AutoWired注解来驱动属性的注入,所以它本质上是手动注入,或者称为半自动注入更合适,因为毕竟我们只加了注解,属性查找、注入的逻辑等核心逻辑还是Spring自动完成的。下面我们对注入模型进行一下总结:

  1. 依赖注入可划分为自动注入和非自动注入两种。自动注入基于构造器、setter方法注入,非自动注入基于属性注入。
  2. 自动注入需要有对应的构造器或者setter方法,作为回调注入的入口,不存在则注入失败。默认的注入模型为AUTOWIRE_NO,需要在bean实例化前修改注入模型,才能是自动注入生效。
  3. @AutoWired、@Resource等注解驱动的属性注入,本质上属于非自动注入。由于注入时使用反射注入,无法保证属性注入的顺序。

1.2 依赖自动注入

 现在我们对注入模型有了初步的了解,知道了Spring是支持自动注入的,只不过需要修改注入模型才能生效。那什么时机修改,怎么修改才能生效呢?首先,必须在bean实例化之前就要修改,如果bean已经实例化了,属性已经注入了,再修改是无法生效的。修改方式则是通过BeanDefinition提供的setAutowireMode()直接设置。基于这两点我们猜测一下,在BeanFactoryPostProcessor#postProcessBeanFactory()的后置处理器中获取BeanDefinition修改,应该是可以生效的,我们试一下:

@Component
public class A {
    private B b;

    public A(){
      System.out.println("无参数的构造方法");
    }
   
    public A(B b){
        System.out.println("只有B的构造方法");
    }

    public void setB(B b){
      System.out.println("set方法注入b");
    }

    public B getB() {
       return b;
    }
}
// 默认的注入模型是不注入,所以在构造方法推断的时候,直接采用无参数的构造方法
@Test
public void defaultModel(){
    AnnotationConfigApplicationContext
          context = new AnnotationConfigApplicationContext("com.autoModel.defaults");
    A a = context.getBean(A.class);
    System.out.println(a.getB());
}
// 修改前输出信息
// 无参数的构造方法
// null

 首先我们看一下,未修改采用默认的注入模型AUTOWIRE_NO时,是无法自动注入属性b的,此时输出内容为null,即默认不注入属性。

// 通过BeanFactoryPostProcessor修改bean的注入模型
public class AutowiredByTypeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition)beanFactory.getBeanDefinition("a");
      beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
   }
}

@Test
public void autowiredByType(){
   AnnotationConfigApplicationContext
         context = new AnnotationConfigApplicationContext();
   context.register(A.class, B.class);
   context.register(AutowiredByTypeBeanFactoryPostProcessor.class);
   context.refresh();
   A a = context.getBean(A.class);
   System.out.println(a);
}
// 输出信息
//无参数的构造方法
// set方法注入b
// com.ivy.autoModel.defaults.A@3cc1435c

 现在我们通过AutowiredByTypeBeanFactoryPostProcessor,先获取a的BeanDefinition,然后修改为AUTOWIRE_BY_TYPE,并提供setB()方法。通过输出发现是可以自动注入属性b的,原理是通过类型B在容器中进行查找,将符合条件的bean注入给该属性,具体逻辑在后续章节详细展开。

 对于AUTOWIRE_BY_NAME,是基于bean名称去Spring容器中查找、匹配、注入的。而AUTOWIRE_CONSTRUCTOR则相对复杂一些,因为可能存在多个构造方法,Spring会根据参数个数、类型等多个方面进行综合评分,最后推断构造方法进行实例化和依赖注入。这里我们不详细展开,感兴趣的小伙伴可以自行查阅资料。

2 @AutoWired依赖注入

@AutoWired可以说是我们日常使用最多的Spring注解之一了,本章节我们一起通过源码解读的方式探索一下他的实现机制和注入逻辑。Spring对@AutoWired的处理大致分为解析 -> 查找 -> 注入 三个核心过程,下面我们顺着这个思路,一起看一下@AutoWired是怎样完成依赖注入的。

2.1 @AutoWired注入属性的解析

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
    try {
       // 通过后置处理器,提供一个:修改合并后的BeanDefinition的机会
       applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
    }
    // ...省略部分代码
}

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof MergedBeanDefinitionPostProcessor) {
         MergedBeanDefinitionPostProces
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值