【Spring源码阅读】Spring容器启动原理(下)-Bean实例的创建和依赖注入

738 阅读5分钟

在上一章节我们介绍了配置资源的加载和注册,此时,Spring容器已经管理了所有的Bean定义相关数据。接下来,就是Bean实例的创建和依赖注入(DI)了。DI(Dependency Injection)依赖注入是指对象是被动接受依赖类而不是自己主动去找,换句话说就是指对象不是从容器中查找它依赖的类,而是在容器实例化对象的时候主动将它依赖的类注入给它

寻找入口

Bean实例的创建和依赖注入在以下两种情况下触发:

  1. 用户第一次调用getBean()方法时,IoC容器触发依赖注入。
  2. Bean的懒加载属性为false时,在容器初始化时自动触发依赖注入。此时还是会调用到getBean()方法。

因此,Bean实例的创建和依赖注入的入口就在getBean()方法中。在上一章节,我们讲过不管是ClassPathXmlApplicationContext还是AnnotationConfigApplicationContext都持有DefaultListableBeanFactory实例。而它们的getBean()就是直接调用了BeanFactorygetBean()方法。DefaultListableBeanFactorygetBean()才是真正的入口。getBean(String)的实现在其抽象父类AbstractBeanFactory中,源码如下: AbstractBeanFactory-getBean

实际调用的是doGetBean()方法: AbstractBeanFactory-doGetBean1

AbstractBeanFactory-doGetBean2

AbstractBeanFactory-doGetBean3

AbstractBeanFactory-doGetBean4

分析以上代码,我们发现有5个重点内容:

  1. 优先从单例缓存中获取Bean实例,如果获取到了,则无需再创建对象。
  2. 优先创建当前Bean依赖的所有Bean
  3. 如果是单例,创建对象之后会放到缓存中。
  4. 如果是原型,每次调用都会创建新的对象。
  5. 如果是自定义scope,则使用自定义scope来管理创建对象的作用域。

不管以上哪种方式,最终创建都想都调用了createBean()方法。

开始实例化

AbstractBeanFactorycreateBean()最终调用了AbstractAutowireCapableBeanFactory的实现,源码如下: AbstractAutowireCapableBeanFactory-createBean

AbstractAutowireCapableBeanFactory-doCreateBean

doCreateBean()的实现中,主要包含4个步骤:

  1. 创建Bean实例。
  2. 如果是单例,缓存单例对象,尽早持有引用,解决循环依赖问题。
  3. 填充Bean属性,依赖注入发生在这里。
  4. 初始化Bean,包括afterPropertiesSet()方法和自定义的初始化方法。

下面我们分别介绍创建Bean实例、填充Bean属性以及初始化Bean的具体实现。

创建Bean实例

创建Bean实例调用了createBeanInstance()方法: AbstractAutowireCapableBeanFactory-createBeanInstance

createBeanInstance()的实现中,主要根据不同情况来进行实例化,主要有以下四种情况:

  1. 调用创建Bean的回调方法实例化。
  2. 调用工厂方法实例化。
  3. 使用容器的自动装配特性,调用匹配的构造方法实例化
  4. 使用默认的无参构造方法实例化

前三种方式比较简单,直接调用匹配的方法实例化即可。但是对于我们最常使用的默认无参构造方法就需要使用相应的实例化策略(JDK的反射机制或者 CGLib)来进行实例化了,在方法getInstantiationStrategy().instantiate()中就具体实现类使用相应的策略来实例化对象。源码如下: AbstractAutowireCapableBeanFactory-instantiateBean

最终调用了SimpleInstantiationStrategyinstantiate()方法: SimpleInstantiationStrategy-instantiate

如果Bean类中无lookupreplaced方式依赖,使用JDK的反射机制进行实例化,否则,使用CGLib进行实例化。instantiateWithMethodInjection()方法调用SimpleInstantiationStrategy的子类 CGLibSubclassingInstantiationStrategy使用CGLib来进行实例化,其源码如下: CglibSubclassingInstantiationStrategy-instantiate

lookupreplaced可参考:Spring5框架之lookup-method与replaced-method方法实现依赖注入

填充Bean属性

在创建Bean实例完成之后,接下来就是填充Bean属性,即我们常说的依赖注入阶段了populateBean()源码如下: AbstractAutowireCapableBeanFactory-populateBean

在正式进行依赖注入之前,首先会调用Bean实例化后置处理器,执行实例化的后置逻辑。对于自动注入的属性,分别可以根据名称或者类型完成注入。最后调用统一的applyPropertyValues()方法完成属性赋值。

初始化Bean

前面已经创建好Bean实例并且也完成了属性注入,下一步就是调用初始化方法做一些初始化操作了,源码如下: AbstractAutowireCapableBeanFactory-initializeBean

初始化方法主要包含4部分内容:

  1. 调用Aware方法。Spring提供可很多Aware接口,如果Bean实现了Aware接口,那么在初始化阶段就会调用某些Aware方法。这里会调用的包括BeanNameAwareBeanClassLoaderAwareBeanFactoryAware的方法。
  2. 调用Bean后置处理器,在初始化之前做一些处理。Spring提供可很多BeanPostProcessor接口,用于在Bean的各个生命周期中织入自定义逻辑。这里会执行应用的BeanPostProcessorpostProcessBeforeInitialization()方法。
  3. 调用初始化方法。包括InitializingBean接口的afterPropertiesSet()以及自定义的初始化方法。
  4. 调用Bean后置处理器,在初始化之后做一些处理。这里会执行应用的BeanPostProcessorpostProcessAfterInitialization()方法。

总结

Spring容器启动主要分为两大阶段,第一阶段是配置资源的加载和注册,第二阶段是Bean的实例化、依赖注入和初始化。在第一阶段,最关注的是BeanDefinition,不管配置方式是xml还是注解,最终都会解析成BeanDefinition对象,并将其注册到容器中。在第一阶段完成之后,容器中已经有了所有Bean的配置信息。第二阶段则是依赖第一阶段处理好的BeanDefinition,从而实现了Bean的实例化、依赖注入和初始化操作。并且,在整个容器启动过程中,Spring提供了各种各样的回调入口,支持用户在各个执行阶段都可以注入自定义处理逻辑。

di.png

原创不易,觉得文章写得不错的小伙伴,点个赞👍 鼓励一下吧~

欢迎关注我的开源项目:一款适用于SpringBoot的轻量级HTTP调用框架