spring-IOC容器源码分析(一)bean初始化流程

在传统的开发步骤中,如果一个类依赖于另一个类的实例才能完成任务的话,都需要开发者手动new一个实例。之后手动设置到任务类中。但spring的出现,为我们提供了一种称为“依赖注入”(Dependency Inject)的机制。bean的实例化在spring container内部完成,开发者只需要从xml或java code来配置bean达到定制实例化bean。而且,可以让我们通过注解的方式,为我们自动注入需要的依赖。
这样,开发者只需要遵循面向接口来开发应用,把实例化具体类和注入依赖的步骤抽离出业务代码,达到解耦的要求。如果哪天需要使用其他的接口实现依赖,只需要将新的实现配置进spring container。其他业务代码无需更改。这篇文章将围绕着spring是如何实现bean的实例化、如何实现依赖注入来展开,剖析其内部运行机制

测试用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 定义一个简单的bean
public class Car {
public String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}

// 定义一个配置类,向Spring bean container注册bean
@Configuration
public class Config {
@Bean
public Car car() {
Car car = new Car();
car.setColor("red");
return car;
}
}

// 编写一个测试类来初始化spring bean container,再从其中获取我们在Config类中定义的bean的实例
public class ContextTest {

public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Car car = context.getBean(Car.class);
System.out.println(car.getColor());
}
}

运行这个用例,spring bean container将在其内部对我们注入的bean进行实例化、初始化两个步骤。由于这里没有定义bean的作用域,因此我们注册的这个bean(即Car)将以单例的形式存在于container中。每一次调用context.getBean(Car.class)返回的都是同一个Car的实例。

源码分析

说明:以下的流程分析,只围绕spring容器实例化singleton bean(即单例)的主流程进行

主流程

对于spring这样一个比较复杂的框架,其中包含了各种繁杂的业务逻辑。如果一开始就直接深究到每一个细节,我们无法对spring container有一个比较全局的观感,因此在这里先把整个主流程画出来,建立一个全局的蓝图。

解析注册BeanDefinition

注册和解析BeanDefinition,发生在AnnotationConfigApplicationContext#register流程中,其方法内部使用了AnnotatedBeanDefinitionReader#register来实现BeanDefinition的解析和注册;而且在实例化AnnotatedBeanDefinitionReader后,立即向container注册了多个BeanPostProcessor的BeanDefinition(应用于bean的实例化过程)

准备BeanFactory

在AnnotationConfigApplicationContext内部,组合了DefaultListableBeanFactory。在prepareBeanFactory(beanFactory)方法的调用过程中,向beanfactory注入了环境变量、环境属性等。而且注入了多个BeanPostProcessor。

调用BeanFactoryPostProcessor

到了这一步,此时的container已经注册了一系列的BeanFactoryPostProcessor、BeanPostProcessor和应用层相关的bean的BeanDefinition(如当前测试用例的Car)。由于此时所有的bean(包括BeanFactoryPostProcessor、BeanPostProcessor已经应用层的bean)都是以BeanDefinition存在于container中,并未实例化。这就提供了一个机会,添加特定的BeanFactoryPostProcessor,让spring在实例化bean之前,可以定制修改BeanDefinition中的一些数据(如常用的PropertyPlaceholderConfigurer,从外部properties文件读取配置,定义bean的属性);
默认情况下,只调用了ConfigurationClassPostProcessor,作用:

  1. 解析配置类,将其定义的bean注入到beanfactory
  2. 利用ConfigurationClassEnhancer增强了配置类的功能
  3. 注册BeanPostProcessor:ImportAwareBeanPostProcessor
注册BeanPostProcessor

继续上一步流程,处理完BeanDefinition之后,对前面流程中注册到beanfactory中的BeanPostProcessor进行实例化,并添加到beanfactory中的BeanPostProcessor处理队列中。经过这一步之后,beanfactory中的BeanPostProcessor队列存在以下BeanPostProcessor:

  1. ApplicationContextAwareProcessor
  2. ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor
  3. PostProcessorRegistrationDelegate$BeanPostProcessorChecker
  4. CommonAnnotationBeanPostProcessor
  5. AutowiredAnnotationBeanPostProcessor
  6. RequiredAnnotationBeanPostProcessor
  7. ApplicationListenerDetector
真正实例化和初始化bean的流程

这个步骤是通过调用DefaultListableBeanFactory#preInstantiateSingletons()方法完成的。实现了预加载所有已注册的bean,这也是ApplicationContext与BeanFactory实现类的区别,在BeanFactory实现类中,只有对一个bean调用getBean(beanname)方法之后才会进行bean的加载。而ApplicationContext则是直接触发其内部的BeanFactory加载所有定义好的bean。
在加载bean的过程中,涉及到三个步骤:

  1. 实例化
  2. 填充属性
  3. 初始化

实例化并初始化bean流程

主流程

实例化

通过调用AbstractAutowireCapableBeanFactory#createBeanInstance来实例化bean(在这之前,spring提供了一个机会可以通过BeanPostProcessor来创建bean,而不是常规的bean实例化,跟AOP相关)。
实例化后,调用了MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition,涉及到的实现类有:

  1. CommonAnnotationBeanPostProcessor,解析类准备@Resource注入需要的InjectionMetadata
  2. AutowiredAnnotationBeanPostProcessor,解析类准备@Autowired注入需要的InjectionMetadata
  3. RequiredAnnotationBeanPostProcessor,空实现
  4. ApplicationListenerDetector,标记了该bean是否为单例
填充bean属性

实例化bean之后,就需要为bean填充bean的属性值了。这一步主要是通过调用:InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation:
可以直接对bean填充属性,之后直接返回。忽略后续的所有填充行为。默认没有进行这一步流程,而是不做任何处理,进入下一个流程

  1. ImportAwareBeanPostProcessor
  2. CommonAnnotationBeanPostProcessor
  3. AutowiredAnnotationBeanPostProcessor
  4. RequiredAnnotationBeanPostProcessor
    InstantiationAwareBeanPostProcessor#postProcessPropertyValues:
    各种自动注入的解析,如@Resource、@Autowired等
  5. ImportAwareBeanPostProcessor
  6. CommonAnnotationBeanPostProcessor
  7. AutowiredAnnotationBeanPostProcessor
  8. RequiredAnnotationBeanPostProcessor
    最后通过applyPropertyValues()进行bean的赋值
初始化

进行到这一步时,bean已经从BeanDefinition实例化为了bean的实例,并且填充了属性。是时候进行一下从外部而来的初始化逻辑了(非BeanDefinition相关的)。主要是:

  1. Aware接口的注入
  2. BeanPostProcessor#postProcessBeforeInitialization处理
  3. InitializingBean#afterPropertiesSet实现类的初始化方法
  4. BeanPostProcessor#postProcessAfterInitialization处理
  5. 注册DisposableBean#destroy,在关闭容器时销毁bean的回调操作

总结

以上我们梳理了spring container加载bean的主体流程,spring为我们提供了几大扩展点:

  1. BeanFactoryPostProcessor
  2. BeanPostProcessor
  3. Aware接口实现
  4. InitializingBean#afterPropertiesSet
  5. DisposableBean#destroy
    为我们提供了改造bean的契机,方便在实际开发过程中定制化bean的加载。常见的有:
  6. PropertyPlaceholderConfigurer作为BeanFactoryPostProcessor的间接实现类,在bean的加载过程中,读取外部properties文件,对已注册的BeanDefinition进行修改,达到了从外部文件配置bean的目的;
  7. 在数据库连接池的bean定义上,我们都会用@PreDestroy指定一个销毁的回调来释放数据库连接资源