本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Bean 的创建过程
容器启动之后, 并不会立刻就实例化相应的 bean. 容器现在仅仅拥有所有对象的 BeanDefinition 信息. 只有当调用了 getBean() 方法来获取某个对象实例的时候, 才有可能触发 Bean 实例化阶段的活动.
BeanFactory 的 getBean() 法可以被显式调用, 也可以在容器内部被隐式调用.
隐式调用有如下两种情况:
- 对于 BeanFactory 容器来说, 对象默认延迟创建. 通常情况下, 当对象 A 被请求而需要第一次实例化的时候, 如果它所依赖的对象 B 之前同样没有被实例化, 那么容器会先实例化对象 A 所依赖的对象. 即首先实例化对象 B. 这种情况是容器内部调用 getBean(), 对于本次请求的请求方是隐式的.
- 对于 ApplicationContext 容器, 虽然它启动之后会实例化所有的 bean. 但是 ApplicationContext 容器在实现的过程中依然遵循 Spring 容器实现流程的两个阶段, 只不过它会在启动阶段的活动完成之后, 紧接着调用注册到该容器的所有 bean 定义的实例化方法 getBean(). 让容器内所有对象全部实例化完成.
之所以说 getBean() 方法是有可能触发 Bean 实例化阶段的活动, 是因为只有当对应某个 bean 定义的 getBean() 方法第一次被调用时, 不管是显式的还是隐式的, Bean 实例化阶段的活动才会被触发, 第二次被调用则会直接返回容器缓存的第一次实例化完的对象实例(prototype 类型 bean 除外). 当 getBean() 方法内部发现该 bean 定义之前还没有被实例化之后, 会通过 createBean() 方法来进行具体的对象实例化, 实例化过程如下图所示.
Spring 容器将对其所管理的对象全部给予统一的生命周期管理.
- 可以在 org.springframework.beans.factory.support.AbstractBeanFactory 类的代码中查看到 getBean() 方法的完整实现逻辑.
- 可以在其子类org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory 的代码中看到 createBean() 方法的实现逻辑.
Bean 的实例化与 BeanWrapper
容器在内部实现的时候, 采用 "策略模式" 来决定采用何种方式初始化 bean 实例. 通常, 可以通过反射或者 CGLIB 动态字节码生成来初始化相应的 bean 实例或者动态生成其子类.
org.springframework.beans.factory.support.InstantiationStrategy 是实例化策略的抽象接口, 其直接子类 SimpleInstantiationStrategy 实现了简单的对象实例化功能, 可以通过反射来实例化对象实例, 但不支持方法注入方式的对象实例化.
CglibSubclassingInstantiationStrategy 继承了 SimpleInstantiationStrategy , 并扩展了通过 CGLIB 的动态字节码生成功能, 该策略实现类可以动态生成某个类的子类, 进而满足了方法注入所需的对象实例化需求. 默认情况下, 容器内部采用的是 CglibSubclassingInstantiationStrategy.
容器只要根据相应 bean 定义的 BeanDefintion 取得实例化信息, 结合 CglibSubclassingInstantiationStrategy 以及不同的 bean 定义类型, 就可以返回实例化完成的对象实例. 但是并不是直接返回构造完成的对象实例, 而是以 BeanWrapper 对构造完成的对象实例进行包裹, 返回相应的 BeanWrapper 实例. 至此, 第一步结束.
BeanWrapper 接口通常在 Spring 框架内部使用, 它有一个实现类 org.springframework.beans.BeanWrapperImpl. 其作用是对某个 bean 进行 "包裹", 然后对这个 "包裹" 的bean 进行操作, 比如设置或者获取 bean 的相应属性值. 而在第一步结束后返回 BeanWrapper 实例就是为了第二步 "设置对象属性".
各色的 Aware 接口
当对象实例化完成并且相关属性以及依赖设置完成之后, Spring 容器会检查当前对象实例是否实现了一系列的以 Aware 命名结尾的接口定义. 如果有, 则将这些 Aware 接口定义中规定的依赖注入给当前对象实例.
这些 Aware 接口为如下几个:
- org.springframework.beans.factory.BeanNameAware. 将该对象实例的 bean 定义对应的 beanName 设置到当前对象实例.
- org.springframework.beans.factory.BeanClassLoaderAware. 将对应加载当前 bean 的 Classloader 注入当前对象实例.
以上几个 Aware 接口只是针对 BeanFactory 类型的容器而言, 对于 ApplicationContext 类型的容器, 也存在几个 Aware 相关接口. 不过在检测这些接口并设置相关依赖的实现机理上, 与以上几个接口处理方式有所不同 ( 使用 BeanFactoryPostProcessor ), 使用的是 BeanPostProcessor 方式. 不过, 设置 Aware 接口这 一步与 BeanPostProcessor 是相邻的, 把这几个接口放到这里一起提及, 也没什么不可以的.
对于 ApplicationContext 类型容器, 容器在这一步还会检查以下几个 Aware 接口并根据接口定义 设置相关依赖.
- org.springframework.context.ResourceLoaderAware
- org.springframework.context.ApplicationEventPublisherAware.
- org.springframework.context.MessageSourceAware.
- org.springframework.context.ApplicationContextAware.
如果 ApplicationContext 容器检测到当前对象实现了ApplicationContextAware 接口, 则会将自身注入当前对象实例.
BeanPostProcessor
BeanPostProcessor 会处理容器内所有符合条件的实例化后的对象实例. 注意 : BeanPostProcessor 是存在于对象实例化阶段, 而 BeanFactoryPostProcessor 则是存在于容器启动阶段. 该接口声明了两个方法, 分别在 两个不同的时机执行.
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
\
BeanPostProcessor 的两个方法中都传入了原来的对象实例的引用, 可以对传入的对象实例执行任何的操作. 通常比较常见的使用 BeanPostProcessor 的场景, 是处理标记接口实现类, 或者为当前对象提供代理实现. 还可以通过 BeanPostProcessor 对当前对象实例做更多的处理. 比如替换当前对象实例或者字节码增强当前对象实例等. Spring 的 AOP则更多地使用 BeanPostProcessor 来为对象生成相应的代理对象.
InitializingBean 和 init-method
org.springframework.beans.factory.InitializingBean 是容器内部广泛使用的一个对象生命周期标识接口, 其定义如下:
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
作用在于, 在对象实例化过程调用过 "BeanPostProcessor 的前置处理" 之后, 会接着检测当前对象是否实现了 InitializingBean 接口, 如果是, 则会调用其 afterPropertiesSet() 方法进一步调整对象实例的状态. 比如, 在有些情况下, 某个业务对象实例化完成后, 还不能处于可以使用状态. 这个时候就可以让该业务对象实现该接口, 并在方法afterPropertiesSet() 中完成对该业务对象的后续处理. 虽然该接口在 Spring 容器内部广泛使用, 但如果真的让我们的业务对象实现这个接口, 则显得 Spring 容器比较具有侵入性. 所以, Spring 提供了另一种方式来指定自定义的对象初始化操作, 那就是在 XML 配置的时候, 使用 init-method 属性. 通过 init-method, 系统中业务对象的自定义初始化操作可以以任何方式命名, 而不再受制于 InitializingBean 的 afterPropertiesSet().
DisposableBean 与 destroy-method
容器接着会检查 singleton 类型的 bean 实例, 看其是否实现了 org.springframework.beans.factory.DisposableBean 接口. 或者其对应的 bean 定义是否通过的 destroy-method 属性指定了自定义的对象销毁方法. 如果有, 就会为该实例注册一个用于对象销毁的回调, 以便在这些 singleton 类型的对象实例销毁之前, 执行销毁逻辑. 最常见到的该功能的使用场景就是在 Spring 容器中注册数据库连接池, 在系统退出后, 连接池应该关闭, 以释放相应资源.
自定义的对象销毁逻辑, 在对象实例初始化完成并注册了相关的回调方法之后, 并不会马上执行. 回调方法注册后, 返回的对象实例即处于使用状态, 只有该对象实例不再被使用的时候, 才会执行相关的自定义销毁逻辑, 此时通常也就是 Spring 容器关闭的时候. 但 Spring 容器在关闭之前, 不会聪明到自动调用这些回调方法. 所以, 需要我们告知容器, 在哪个时间点来执行对象的自定义销毁方法. 对于 BeanFactory 容器来说. 我们需要在独立应用程序的主程序退出之前, 或者其他被认为是合适的情况下, 调用 ConfigurableBeanFactory 提供的 destroySingletons() 方法销毁容器中管理的所有 singleton 类型的对象实例.
如果不能在合适的时机调用 destroySingletons(), 那么所有实现了 DisposableBean 接口的对象实例或者声明了 destroy-method 的 bean 定义对应的对象实例, 它们的自定义对象销毁逻辑就形同虚设, 因为根本就不会被执行. 对于 ApplicationContext 容器来说. 道理是一样的. 但 AbstractApplicationContext 为我们 提供了 registerShutdownHook()方法, 该方法底层使用标准的 Runtime 类的 addShutdownHook() 方式来调用相应 bean 对象的销毁逻辑, 从而保证在 Java 虚拟机退出之前, 这些 singtleton 类型的 bean 对象实例的自定义销毁逻辑会被执行. 当然 AbstractApplicationContext 注册的 shutdownHook 不只是调用对象实例的自定义销毁逻辑, 也包括ApplicationContext 相关的事件发布等.
至此, bean 走完了它在容器中“光荣”的一生.