文前说明
作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。
本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。
1. 概述
- Spring 并不是一启动容器就开启 bean 的实例化进程,只有当客户端通过显示或者隐式的方式调用
BeanFactory
的getBean()
方法来请求某个实例对象的时候,才会触发相应 bean 的实例化进程。- 如果选择直接使用
ApplicationContext
容器,该容器启动时会立刻调用注册到该容器所有 bean 定义的实例化方法。 - 对于
BeanFactory
容器而言,也不是所有的getBean()
方法都会触发实例化进程,比如 singleton 类型的 bean,该类型的 bean 只会在第一次调用getBean()
的时候才会触发,而后续的调用则会直接返回容器缓存中的实例对象。
- 如果选择直接使用
-
getBean()
方法,是 bean 实例化进程的入口,真正的实现逻辑是在AbstractAutowireCapableBeanFactory
的doCreateBean()
中实现。
- Spring 容器将会对其所有管理的 Bean 对象全部给予一个统一的 生命周期管理,同时在这个阶段也可以对其进行干涉(比如对 bean 进行增强处理,对 bean 进行篡改)。
2. 原理
bean 的实例化
- 在
doCreateBean()
方法中,首先进行 bean 实例化工作,主要由createBeanInstance()
方法实现,该方法返回一个BeanWrapper
对象。- 这个时候的 Bean 还不能够被使用,最基本的属性都没有设置。实际开发过程中,一般都不会直接使用该类,而是通过
BeanFactory
隐式使用。 -
BeanWrapper
接口有一个默认实现类BeanWrapperImpl
,其主要作用是对 Bean 进行包裹,然后对这个包裹的 Bean 进行操作,比如后续注入 Bean 属性。 - 在实例化 Bean 过程中,Spring 采用 策略模式 决定采用哪种方式实例化 Bean,一般有 反射 和 CGLIB 动态字节码 两种方式。
- 这个时候的 Bean 还不能够被使用,最基本的属性都没有设置。实际开发过程中,一般都不会直接使用该类,而是通过
-
InstantiationStrategy
定义了 Bean 实例化策略的抽象接口。- 其子类
SimpleInstantiationStrategy
提供了基于反射来实例化对象的功能,但是不支持 方法注入方式的对象实例化。 -
CglibSubclassingInstantiationStrategy
继承SimpleInstantiationStrategy
,除了拥有反射实例化对象的功能外,还提供了 通过 CGLIB 的动态字节码的功能进而支持方法注入所需的对象实例化 需求。 - 默认情况下,Spring 采用
CglibSubclassingInstantiationStrategy
。
- 其子类
- 具体可查看 【Spring 笔记】创建 Bean 相关整理(上) 和 【Spring 笔记】创建 Bean 相关整理(下)
激活 Aware
- 当 Spring 完成 bean 对象实例化并且设置完相关属性和依赖后,则开始 bean 的初始化进程(
initializeBean()
),初始化第一个阶段是检查当前 bean 对象是否实现了一系列以Aware
结尾的的接口。- Aware 接口为 Spring 容器的核心接口,具有标识作用,实现了该接口的 bean 是具有被 Spring 容器通知的能力,通知的方式是采用 回调 的方式。
- 在初始化阶段主要是感知
BeanNameAware
、BeanClassLoaderAware
、BeanFactoryAware
。
// AbstractAutowireCapableBeanFactory.java
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
// BeanNameAware
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
// BeanClassLoaderAware
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
// BeanFactoryAware
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
Aware 接口 | 说明 |
---|---|
BeanNameAware | 对该 bean 对象定义的 beanName 设置到当前对象实例中 |
BeanClassLoaderAware | 将当前 bean 对象相应的 ClassLoader 注入到当前对象实例中 |
BeanFactoryAware |
BeanFactory 容器会将自身注入到当前对象实例中,这样当前对象就会拥有一个 BeanFactory 容器的引用。 |
LoadTimeWeaverAware | 加载 Spring Bean 时织入第三方模块,如 AspectJ。 |
BootstrapContextAware | 资源适配器 BootstrapContext,如 JCA,CCI。 |
ResourceLoaderAware | 底层访问资源的加载器。 |
PortletConfigAware | PortletConfig。 |
PortletContextAware | PortletContext。 |
ServletConfigAware | ServletConfig。 |
ServletContextAware | ServletContext。 |
MessageSourceAware | 国际化。 |
ApplicationEventPublisherAware | 应用事件。 |
NotificationPublisherAware | JMX 通知。 |
- 具体可查看 【Spring 笔记】Aware 接口相关整理。
BeanPostProcessor
- 初始化第二个阶段则是
BeanPostProcessor
增强处理,在该阶段BeanPostProcessor
会处理当前容器内所有符合条件的实例化后的 bean 对象。- 它主要是对 Spring 容器提供的 bean 实例对象进行有效的扩展,允许 Spring 在初始化 bean 阶段对其进行定制化修改,如处理标记接口或者为其提供代理实现。
-
BeanPostProcessor
接口提供了两个方法,在不同的时机执行,分别是 前置处理 和 后置处理。
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
InitializingBean 和 init-method
-
InitializingBean
是一个接口,为 Spring Bean 的初始化提供了一种方式,有一个afterPropertiesSet()
方法,在 bean 的初始化进程中会判断当前 bean 是否实现了InitializingBean
,如果实现则调用afterPropertiesSet()
方法,进行初始化工作。 - 然后再检查是否也指定了 init-method ,如果指定了则通过反射机制调用指定的 init-method 方法。
// AbstractAutowireCapableBeanFactory.java
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
// 首先会检查是否是 InitializingBean ,如果是的话需要调用 afterPropertiesSet()
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) { // 安全模式
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
// 属性初始化的处理
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
} else {
// 属性初始化的处理
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
// 判断是否指定了 init-method(),
// 如果指定了 init-method(),则再调用制定的init-method
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
// 激活用户自定义的初始化方法
// 利用反射机制执行
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
- 虽然两种方式都可以实现初始化定制化,但是更加推崇 init-method 方式,因为对于
InitializingBean
接口而言,需要 bean 去实现接口,这样会显得 Spring 具有一定的侵入性。 - init-method 采用反射的方式,所以执行效率上相对于
InitializingBean
接口回调的方式可能会低一些。 - 具体可查看 【Spring 笔记】InitializingBean 和 init-method 相关整理。
DisposableBean 和 destroy-method
-
DisposableBean
和 destroy-method 用于对象的自定义销毁工作。- 当一个 bean 对象经历了实例化、设置属性、初始化阶段,那么该 bean 对象就可以供容器使用(调用的过程)。当完成调用后,如果是 singleton 类型的 bean ,则会看当前 bean 是否应实现了
DisposableBean
接口或者配置了 destroy-method 属性,如果是则会为该实例注册一个用于对象销毁的回调方法,便于在这些 singleton 类型的 bean 对象销毁之前执行销毁逻辑。 - 并不是对象完成调用后就会立刻执行销毁方法,因为这个时候 Spring 容器还处于运行阶段,只有当 Spring 容器关闭的时候才会去调用。
- 对于
BeanFactory
容器而言,需要主动调用destroySingletons()
方法,通知BeanFactory
容器去执行相应的销毁方法。 - 对于
ApplicationContext
容器而言,调用registerShutdownHook()
方法。
- 当一个 bean 对象经历了实例化、设置属性、初始化阶段,那么该 bean 对象就可以供容器使用(调用的过程)。当完成调用后,如果是 singleton 类型的 bean ,则会看当前 bean 是否应实现了
2.1 用例验证
- 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="test" class="Test" init-method="initMethod" destroy-method="destroyMethdo">
<property name="test" value="test"/>
</bean>
</beans>
- 用例
public class Test implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, BeanPostProcessor,
InitializingBean, DisposableBean {
private String test;
public void setTest(String test) {
System.out.println("setTest 属性注入....");
this.test = test;
}
public Test() { // 构造方法
System.out.println("调用构造函数...");
}
public void run() {
System.out.println("run 方法调用...");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("BeanClassLoaderAware 被调用...");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware 被调用...");
}
@Override
public void setBeanName(String s) {
System.out.println("BeanNameAware 被调用...");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean destroy 被调用...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean afterPropertiesSet 被调用...");
}
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("BeanPostProcessor postProcessBeforeInitialization 被调用...");
return o;
}
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println("BeanPostProcessor postProcessAfterInitialization 被调用...");
return o;
}
public void initMethod() {
System.out.println("init-method 被调用...");
}
public void destroyMethdo() {
System.out.println("destroy-method 被调用...");
}
public static void main(String[] args) {
ClassPathResource resource = new ClassPathResource("spring.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
factory.addBeanPostProcessor(new Test());
Test test = (Test) factory.getBean("test");
test.run();
System.out.println("方法调用完成,容器开始关闭....");
// 关闭容器
factory.destroySingletons();
}
}
/**
print
调用构造函数...
调用构造函数...
setTest 属性注入....
BeanNameAware 被调用...
BeanClassLoaderAware 被调用...
BeanFactoryAware 被调用...
BeanPostProcessor postProcessBeforeInitialization 被调用...
InitializingBean afterPropertiesSet 被调用...
init-method 被调用...
BeanPostProcessor postProcessAfterInitialization 被调用...
run 方法调用...
方法调用完成,容器开始关闭....
DisposableBean destroy 被调用...
destroy-method 被调用...
**/
- Spring Bean 的生命周期过程。
- Spring 容器根据 实例化策略 对 Bean 进行实例化。
- 实例化完成后,如果该 bean 设置了一些属性的话,则利用 set 方法设置一些属性。
- Bean 实现了
BeanNameAware
接口,则调用setBeanName()
方法。 - Bean 实现了
BeanClassLoaderAware
接口,则调用setBeanClassLoader()
方法。 - Bean 实现了
BeanFactoryAware
接口,则调用setBeanFactory()
方法。 - 容器注册了
BeanPostProcessor
,则调用postProcessBeforeInitialization()
方法,完成 bean 的前置处理。 - Bean 实现了
InitializingBean
接口,则调用afterPropertiesSet()
方法。 - Bean 配置了 init-method 方法,则调用其指定的方法。
- 容器注册了
BeanPostProcessor
则调用postProcessAfterInitialization()
方法,完成 bean 的后置处理。 - 对象完成初始化,开始方法调用。
- 容器进行关闭之前,如果该 bean 实现了
DisposableBean
接口,则调用destroy()
方法。 - 容器进行关闭之前,如果该 bean 配置了 destroy-method,则调用其指定的方法。
网友评论