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

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

  • 原理解析
    • 依赖注入
      • PropertyValues、PropertyValue、PropertyAccessor
      • byName
      • byType
      • @Autowired
    • bean的初始化
  • 源码走读
    • 依赖注入
      • populateBean方法
      • autowireByName
      • autowireByType方法
      • AutowiredAnnotationBeanPostProcessor#postProcessProperties
    • 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. 初始化

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

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

原理解析

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

依赖注入

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

PropertyValues、PropertyValue、PropertyAccessor

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

下面是这三者的关系图:

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

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

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

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

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

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

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

byName

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

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

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源码解析,依赖注入和bean的初始化_第6张图片

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

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

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

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

@Autowired

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

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

bean的初始化

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

一共四个步骤:

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

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

源码走读

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

依赖注入

populateBean方法

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

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

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

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

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

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

populateBean的整体逻辑如下图:

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

autowireByName

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

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

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

进入pvs.add(propertyName, bean)。

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

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

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

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

autowireByType方法

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

简单易懂值得收藏的Spring源码解析,依赖注入和bean的初始化_第19张图片
可以看到跟byName的区别就是获取依赖对象不再是直接通过getBean方法,而是调用了resolveDependency方法。

简单易懂值得收藏的Spring源码解析,依赖注入和bean的初始化_第20张图片
resolveDependency方法又调用了doResolveDependency方法。

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

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

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

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

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

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

简单易懂值得收藏的Spring源码解析,依赖注入和bean的初始化_第24张图片
简单易懂值得收藏的Spring源码解析,依赖注入和bean的初始化_第25张图片
最后取得最合适的beanName后,还是getBean(beanName)获取依赖对象。

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

AutowiredAnnotationBeanPostProcessor#postProcessProperties

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

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

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

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

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

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

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

先来看AutowiredFieldElement#inject方法。
简单易懂值得收藏的Spring源码解析,依赖注入和bean的初始化_第30张图片
在AutowiredFieldElement#inject方法里面,可以看到又是调用beanFactory的resolveDependency方法,所以跟byType的逻辑一样。

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

简单易懂值得收藏的Spring源码解析,依赖注入和bean的初始化_第31张图片
再来看下AutowiredMethodElement#inject方法。

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

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

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

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

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

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

bean的初始化

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

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

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

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

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

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

总结

以上就是依赖注入和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方法回调

你可能感兴趣的:(Spring,spring,java,servlet,后端,spring源码解析)