在传统的开发步骤中,如果一个类依赖于另一个类的实例才能完成任务的话,都需要开发者手动new一个实例。之后手动设置到任务类中。但spring的出现,为我们提供了一种称为“依赖注入”(Dependency Inject)的机制。bean的实例化在spring container内部完成,开发者只需要从xml或java code来配置bean达到定制实例化bean。而且,可以让我们通过注解的方式,为我们自动注入需要的依赖。
这样,开发者只需要遵循面向接口来开发应用,把实例化具体类和注入依赖的步骤抽离出业务代码,达到解耦的要求。如果哪天需要使用其他的接口实现依赖,只需要将新的实现配置进spring container。其他业务代码无需更改。这篇文章将围绕着spring是如何实现bean的实例化、如何实现依赖注入来展开,剖析其内部运行机制
测试用例
// 定义一个简单的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,作用:
- 解析配置类,将其定义的bean注入到beanfactory
- 利用ConfigurationClassEnhancer增强了配置类的功能
- 注册BeanPostProcessor:ImportAwareBeanPostProcessor
注册BeanPostProcessor
继续上一步流程,处理完BeanDefinition之后,对前面流程中注册到beanfactory中的BeanPostProcessor进行实例化,并添加到beanfactory中的BeanPostProcessor处理队列中。经过这一步之后,beanfactory中的BeanPostProcessor队列存在以下BeanPostProcessor:
- ApplicationContextAwareProcessor
- ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor
- PostProcessorRegistrationDelegate$BeanPostProcessorChecker
- CommonAnnotationBeanPostProcessor
- AutowiredAnnotationBeanPostProcessor
- RequiredAnnotationBeanPostProcessor
- ApplicationListenerDetector
真正实例化和初始化bean的流程
这个步骤是通过调用DefaultListableBeanFactory#preInstantiateSingletons()方法完成的。实现了预加载所有已注册的bean,这也是ApplicationContext与BeanFactory实现类的区别,在BeanFactory实现类中,只有对一个bean调用getBean(beanname)方法之后才会进行bean的加载。而ApplicationContext则是直接触发其内部的BeanFactory加载所有定义好的bean。
在加载bean的过程中,涉及到三个步骤:
- 实例化
- 填充属性
- 初始化
实例化并初始化bean流程
主流程
实例化
通过调用AbstractAutowireCapableBeanFactory#createBeanInstance来实例化bean(在这之前,spring提供了一个机会可以通过BeanPostProcessor来创建bean,而不是常规的bean实例化,跟AOP相关)。
实例化后,调用了MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition,涉及到的实现类有:
- CommonAnnotationBeanPostProcessor,解析类准备@Resource注入需要的InjectionMetadata
- AutowiredAnnotationBeanPostProcessor,解析类准备@Autowired注入需要的InjectionMetadata
- RequiredAnnotationBeanPostProcessor,空实现
- ApplicationListenerDetector,标记了该bean是否为单例
填充bean属性
实例化bean之后,就需要为bean填充bean的属性值了。这一步主要是通过调用:InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation: 可以直接对bean填充属性,之后直接返回。忽略后续的所有填充行为。默认没有进行这一步流程,而是不做任何处理,进入下一个流程
- ImportAwareBeanPostProcessor
- CommonAnnotationBeanPostProcessor
- AutowiredAnnotationBeanPostProcessor
- RequiredAnnotationBeanPostProcessor InstantiationAwareBeanPostProcessor#postProcessPropertyValues: 各种自动注入的解析,如@Resource、@Autowired等
- ImportAwareBeanPostProcessor
- CommonAnnotationBeanPostProcessor
- AutowiredAnnotationBeanPostProcessor
- RequiredAnnotationBeanPostProcessor 最后通过applyPropertyValues()进行bean的赋值
初始化
进行到这一步时,bean已经从BeanDefinition实例化为了bean的实例,并且填充了属性。是时候进行一下从外部而来的初始化逻辑了(非BeanDefinition相关的)。主要是:
- Aware接口的注入
- BeanPostProcessor#postProcessBeforeInitialization处理
- InitializingBean#afterPropertiesSet实现类的初始化方法
- BeanPostProcessor#postProcessAfterInitialization处理
- 注册DisposableBean#destroy,在关闭容器时销毁bean的回调操作
总结
以上我们梳理了spring container加载bean的主体流程,spring为我们提供了几大扩展点:
- BeanFactoryPostProcessor
- BeanPostProcessor
- Aware接口实现
- InitializingBean#afterPropertiesSet
- DisposableBean#destroy 为我们提供了改造bean的契机,方便在实际开发过程中定制化bean的加载。常见的有:
- PropertyPlaceholderConfigurer作为BeanFactoryPostProcessor的间接实现类,在bean的加载过程中,读取外部properties文件,对已注册的BeanDefinition进行修改,达到了从外部文件配置bean的目的;
- 在数据库连接池的bean定义上,我们都会用@PreDestroy指定一个销毁的回调来释放数据库连接资源