简单易懂值得收藏的Spring源码解析,依赖注入和bean的初始化

往期文章:

  1. 人人都能看懂的Spring底层原理,看完绝对不会懵逼
  2. 简单易懂的Spring扩展点详细解析,看不懂你来打我
  3. 人人都能看懂的Spring源码解析,配置解析与BeanDefinition加载注册
  4. 简单易懂又非常牛逼的Spring源码解析,ConfigurationClassPostProcessor的具体逻辑
  5. 简单易懂又非常牛逼的Spring源码解析,推断构造与bean的实例化

上一篇文章讲到了bean的实例化过程。bean的实例化完成之后,就要对bean进行属性赋值,然后进行初始化。

一个bean从配置到初始化完成放入容器中,要经历四个阶段:

  1. 从bean配置到BeanDefinition
  2. bean实例化
  3. 依赖注入
  4. 初始化

在这里插入图片描述

前两个阶段已经在前两篇文章介绍完,因此本篇文章将重点介绍后面两个阶段,bean的依赖注入初始化这两大过程。

原理解析

下面对依赖注入和bean的初始化这两个过程的原理进行解析。

依赖注入

首先是依赖注入的原理解析。

PropertyValues、PropertyValue、PropertyAccessor

首先要理解与依赖注入相关的三个重要的类PropertyValuesPropertyValuePropertyAccessor

下面是这三者的关系图:

在这里插入图片描述

Spring再bean的依赖注入的时候,不是处理到一个属性就给一个属性赋值的,而是会把一个bean的属性,以及该属性依赖的对象封装到一个PropertyValue对象中,然后把PropertyValue放入到PropertyValues中,PropertyValues相当于是存放PropertyValue的容器,里面其实是一个List

收集好了一个bean的所有的PropertyValue到PropertyValues后,将交给PropertyAccessor属性访问器处理。PropertyAccessor通过 method.invoke(…) 或者 field.set(…) 等反射的方法进行属性赋值。

在这里插入图片描述

PropertyAccessor是一个接口,然后BeanWrapper接口又继承了该接口,所以最终实现类就是BeanWrapperImpl

在这里插入图片描述

那Spring是如何解析bean的属性依赖关系,生成PropertyValue并放入PropertyValues中的呢?下面将进行介绍。

byName

首先是byName模式的依赖注入,就是通过属性名propertyName作为beanName,调用**getBean(propertyName)**方法获取依赖对象,然后和propertyName一起封装成PropertyValue对象,放入PropertyValues中。

在这里插入图片描述

byType

byType模式的依赖注入比byName稍微复杂:

  1. 先通过类型从容器中获取与该类型匹配的所有beanName,返回一个beanName数组candidateNames
  2. 遍历candidateNames,获取对应的Class对象放入到一个Map中
  3. 遍历结束后,该Map如果size大于1,则要从该Map中推断出一个最合适的beanName。推断逻辑就是@Primary注解修饰的优先考虑,如果没有就看有没有@Priority注解修饰的,也没有就寻找beanName和属性名匹配的。然后通过beanName调用getBean获取依赖的对象。
  4. 最后和属性名一起封装成PropertyValue对象放入PropertyValues中

在这里插入图片描述

但是在上面这个逻辑之前,Spring还会回调AutowireCandidateResolver接口实现类的getSuggestedValue方法,该接口是一个扩展点,允许我们对依赖注入上做自定义处理,如果该方法返回值不为空,就会注入我们给定的值,不会往下走上面的逻辑。

在这里插入图片描述

所以byType的整体逻辑就如下图:

在这里插入图片描述

@Autowired

除了byType和byName两种模式,还有就是通过**@Autowired**注解进行依赖注入。@Autowired注解修饰的属性的依赖注入又 AutowiredAnnotationBeanPostProcessor 这个bean后置处理器进行处理,处理逻辑与byType一致

在这里插入图片描述

bean的初始化

接下来是bean的初始化的原理解析。

一共四个步骤:

  1. Aware接口的回调
  2. bean后置处理器before方法的回调
  3. bean的初始化方法的回调
  4. bean后置处理器after方法的回调

在这里插入图片描述

源码走读

接下来进行源码走读,对上面的分析进行验证。

依赖注入

populateBean方法

依赖注入处理逻辑的入口,位于AbstractAutowireCapableBeanFactory的doCreateBean方法里面的populateBean(beanName, mbd, instanceWrapper) 这一行代码。

在这里插入图片描述

populateBean方法里面可以看到byName和byType两种模式的依赖注入的入口,autowireByName方法处理byName模式的依赖注入,autowireByType方法处理byType模式的依赖注入。

在这里插入图片描述

下面还有对@Autowired注解修饰的属性的依赖注入的处理入口,实现了InstantiationAwareBeanPostProcessor接口的后置处理器,就调用postProcessProperties方法。

在这里插入图片描述

populateBean的整体逻辑如下图:

在这里插入图片描述

autowireByName

进入autowireByName方法,看一下byName模式依赖注入的处理逻辑。

在这里插入图片描述

**unsatisfiedNonSimpleProperties(mbd, bw)获取当前bean所有待注入的属性 String[] propertyNames,然后遍历propertyNames,通过getBean(propertyName)以propertyName作为beanName从容器中获取,然后调用pvs.add(propertyName, bean)**添加到PropertyValues中。

进入pvs.add(propertyName, bean)。

在这里插入图片描述

在这里插入图片描述

可以看到就是把属性名和依赖的对象封装成一个PropertyValue对象,放到PropertyValues里面的一个List中。

在这里插入图片描述

autowireByType方法

然后在进去autowireByType方法,看看byType模式的依赖注入的处理逻辑。

在这里插入图片描述
可以看到跟byName的区别就是获取依赖对象不再是直接通过getBean方法,而是调用了resolveDependency方法。

在这里插入图片描述
resolveDependency方法又调用了doResolveDependency方法。

在这里插入图片描述

doResolveDependency方法里面首先调用AutowireCandidateResolver的getSuggestedValue方法尝试获取给定的依赖对象或值value,如果返回的value不为空,就以该value作为要注入当当前属性的值。

在这里插入图片描述

然后Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor)这一行代码就是通过beanName获取Class对象,然后返回一个Map,key是beanName,value是对应的Class对象。

在这里插入图片描述

这一行代码就是判断如果Map的size大于1,则推断出一个合适的beanName。

在这里插入图片描述
在这里插入图片描述
最后取得最合适的beanName后,还是getBean(beanName)获取依赖对象。

在这里插入图片描述

AutowiredAnnotationBeanPostProcessor#postProcessProperties

接下来看一下对@Autowired注解修饰的属性的依赖注入,这里会回调所有InstantiationAwareBeanPostProcessor的postProcessProperties方法,AutowiredAnnotationBeanPostProcessor实现了InstantiationAwareBeanPostProcessor接口,因此会进入AutowiredAnnotationBeanPostProcessor的postProcessProperties方法

在这里插入图片描述

进入AutowiredAnnotationBeanPostProcessor的postProcessProperties方法,从metadata.inject(bean, beanName, pvs)这一行代码进去。

在这里插入图片描述

继续,从element.inject(target, beanName, pvs)这一行代码进行。

在这里插入图片描述

如果@Autowired修饰在字段上,会进入到AutowiredFieldElement#inject方法;如果是@Autowired注解修饰在set方法上,会进入到AutowiredMethodElement#inject方法。

先来看AutowiredFieldElement#inject方法。
在这里插入图片描述
在AutowiredFieldElement#inject方法里面,可以看到又是调用beanFactory的resolveDependency方法,所以跟byType的逻辑一样。

如果返回结果不为空,就通过field.set(bean, value)反射注入。

在这里插入图片描述
再来看下AutowiredMethodElement#inject方法。

在这里插入图片描述

还是调用了beanFactory的resolveDependency方法,与byType的逻辑一样。

如果返回结果不为空,则调用method.invoke(bean, arguments)进行反射注入。

在这里插入图片描述

在这里插入图片描述

依赖注入的代码走读就到这里,下面就是bean的初始化。

bean的初始化

bean的初始化的入口位于AbstractAutowireCapableBeanFactory#doCreateBean方法里面的 exposedObject = initializeBean(beanName, exposedObject, mbd) 这一行代码。

在这里插入图片描述

进入initializeBean(beanName, exposedObject, mbd)方法。

在这里插入图片描述

可以看到就是原来分析里面的四步,非常清晰。**invokeAwareMethods(beanName, bean)**就是Aware接口的回调,**applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)**就是bean后置处理器before方法的回调,invokeInitMethods(beanName, wrappedBean, mbd) 就是bean的初始化方法的回调,applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName) 就是bean后置处理器after方法的回调。

在这里插入图片描述

总结

以上就是依赖注入和bean的初始化的全部内容,下面做一个简单总结。

依赖注入:

  • byName模式会通过属性名调用getBean方法获取依赖对象,然后封装为PropertyValue放入到PropertyValues中,后面统一进行注入处理。
  • byType会先回调AutowiredCandidateResolver的getSuggestedValue方法,如果返回为null,则根据类型获取与该类型匹配的所有beanName,然后再弄出一个key为beanName,value为Class对象的Map,如果该Map的size大于1,则要推断出一个最合适的beanName,然后通过getBean获取依赖对象,然后封装为PropertyValue放入到PropertyValues中,后面统一进行注入处理。
  • @Autowired注解修饰的字段的依赖注入,则通过bean后置处理器AutowiredAnnotationBeanPostProcessor进行处理,里面的逻辑与byType一致。

bean的初始化:

  1. Aware接口的回调
  2. bean后置处理器的before方法回调
  3. bean的初始化方法的回调
  4. bean后置处理器的after方法回调
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring框架通过使用依赖注入(DI)来实现解耦。DI允许外部实体在构造函数,字段或者集合属性注入相应的依赖,从而使得类的实例化更加简单,代码更加清晰,维护更加容易。Spring框架实现DI的关键有三个:控制反转(IoC)、面向切面编程(AOP)和依赖查找(DL)。 ### 回答2: Spring框架是一个开源的Java应用开发框架,它使用了依赖注入的原理来管理对象之间的依赖关系。所谓依赖注入,就是让程序员不再需要手动创建和管理对象之间的关系,而是由框架来自动完成。 在Spring框架中,我们首先需要定义好我们的Java类,声明它们之间的依赖关系。我们可以使用注解的方式,在需要依赖的属性或者构造方法上加上注解,告诉框架这个属性或者参数需要注入一个对象。 当我们启动程序的时候,Spring框架会根据我们的配置信息,遍历所有的Java类,解析其中的注解信息。然后会根据这些信息创建一个对象的实例,并且将需要注入的属性或者参数自动赋值。这个过程是通过Java的反射机制来实现的。 具体来说,Spring框架会根据注解上的信息,找到合适的对象实例,然后通过调用对象的构造方法或者设值方法,将实例注入到被依赖的属性或者参数中。这样,我们就完成了对象之间的依赖关系的建立,可以方便地使用它们进行开发和业务处理。 借助依赖注入,我们不再需要手动创建和管理对象之间的依赖关系,大大简化了对象之间的耦合度。我们只需要关注对象的功能实现,而不需要过多关心它的依赖关系。这样可以提高开发效率,同时也方便了程序的维护和修改。 ### 回答3: Spring框架是一个用于简化Java开发的框架,其中的依赖注入是其中的一个核心特性。 依赖注入是将对象之间的依赖关系交由框架来管理,而不是由开发人员手动创建和管理。在Spring中,依赖注入是通过配置文件或注解的方式来实现的。 首先,需要将要注入的类所对应的bean配置为一个Springbean,这样框架就能够管理这个对象的生命周期。配置文件通常是一个XML文件,其中包含了对Bean的定义和属性的设置。 接下来,需要在需要注入的类中声明需要注入的属性,并为这些属性提供setter方法。Spring框架在启动时会扫描配置文件,找到需要注入的类,并创建对应的对象。 当需要使用某个对象时,Spring会自动将需要注入的属性通过反射的方式注入到对象中,而不需要开发人员手动创建和设置依赖关系。 通过注入,对象之间的依赖关系被解耦,每个对象只需要关注自己的业务逻辑,而不需要关心如何获取依赖的对象。这样可以提高代码的可维护性和可测试性,并且减少了对象之间的紧耦合。 总的来说,Spring框架的依赖注入是通过配置文件或注解的方式来管理对象之间的依赖关系,框架会自动将需要注入的属性注入到对象中。这样可以简化开发过程,提高代码的可维护性和可测试性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值