【README】
本文总结自《spring揭秘》,作者王福强,非常棒的一本书,墙裂推荐;
spring容器根据配置元素组装可用系统分2个阶段,包括spring容器启动, springbean实例化阶段; 本文详细分析springbean实例化阶段;
【补充】spring容器启动阶段,参见 https://blog.csdn.net/PacosonSWJTU/article/details/141181844
【1】spring构建应用系统分2个阶段
【1.1】spring容器启动阶段
容器启动阶段:加载配置元数据(xml文件),然后使用工具类如 BeanDefinitionReader对加载的配置元数据进行解析,并将结果编组为 BeanDefinition ,注册到相应的 BeanDefinitionRegistry,这样容器启动工作就完成了
【1.2】springbean实例化阶段
bean实例化阶段: 容器会首先检查所请求的对象之前是否已经初始化,若没有,则根据注册的BeanDefinition实例化bean,并为其注入依赖。 如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它; 当该对象被装配完成后 ,容器会立即将其返回给请求方使用;
【2】springbean生命周期概述
-
容器在启动阶段,仅仅收集了 BeanDefinition,来保存实例化阶段将要用到的必要信息;只有当请求方请求某个对象实例时,才有可能调用getBean()方法触发bean实例化;
-
BeanFactory的getBean方法可以被客户端对象显式调用,也可以在容器内部隐式调用,隐式调用有如下两种情况:
- 对于BeanFactory, 对象实例化默认采用延迟初始化;即当对象被请求时,才实例化;
- 对于ApplicationContext,容器启动完成后会实例化所有bean;即容器启动完成后,紧接着调用getBean() 方法实例化所有bean;
- 具体的, ApplicationContext容器启动完成后,调用refresh()方法,refresh方法再调用getBean()方法对所有对象全部实例化;
- 调用getBean()获取bean时,若该bean没有被实例化,则getBea()调用createBean() 方法来进行具体的实例化,bean实例化流程如下(bean生命周期)。
【3】springbean生命周期过程
【3.1】第1步-实例化bean对象
通过new创建对象(如工厂方法),或通过反射或CGLIB动态字节码创建对象或动态生成其子类;
【3.2】第2步-设置对象属性
通过构造器注入或setter方法注入以绑定对象间依赖关系(绑定对象与被依赖对象间的关系);
【3.3】 第3步-检查Aware接口并设置相关依赖
spring容器会检查当前对象是否实现以Aware结尾的接口定义; 如果是,则将Aware接口中规定的依赖对象注入(装配)给当前对象;
BeanFactory容器中bean可以实现的Aware接口清单如下:
- BeanNameAware:把beanName注入(装配)到当前对象;
- BeanClassLoaderAware: 把ClassLoader注入(装配)到当前对象;
- BeanFactoryAware: 把BeanFactory注入(装配)到当前对象;
ApplicationContext容器中bean可以实现的Aware接口清单如下:
- ResourceLoaderAware: 把 ResourceLoader注入(装配)到当前对象;
- ApplicationEventPublisherAware: 把 ApplicationEventPublisher 注入(装配)到当前对象;
- MessageSourceAware: 把 MessageSource注入(装配)到当前对象;
- ApplicationContextAware: 把ApplicationContext注入(装配)到当前对象;
- 补充: 因为ApplicationContext 实现了 ResourceLoader, ApplicationEventPublisher ,MessageSource接口,所以上述注入过程实际上是注入ApplicationContext本身给当前对象;可以参见 ApplicationContextAwareProcessor 这个 BeanPostProcessor 实现类;
应用场景: 使得非spring框架的bean(如业务bean)可以获取spring框架bean的引用,如获取ApplicationContext spring容器,然后通过调用ApplicationContext#get()方法获取某个bean实例;
【3.4】第4步-BeanPostProcessor前置处理
BeanPostProcessor有2个方法:
- postProcessBeforeInitialization: bean初始化前置处理;
- postProcessAfterInitialization:bean初始化后置处理;
- 补充:bean初始化逻辑包括 调用InitializingBean#afterPropertiesSet() 和 通过xml配置init-method方法;
BeanPostProcessor应用场景:
- 处理标记接口实现类
- 为当前对象提供代理实现;
- 替换当前对象实例;
- 字节码增强当前对象实例;
- SpringAOP为对象生成相应的代理对象;如 BeanNameAutoProxyCreator的BeanPostProcessor实现类;
- 各种资源密码加解密; 如连接mysql, 中间件(如kafka,es等)服务器;
BeanPostProcessor定义如下:
public interface BeanPostProcessor {@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Nullabledefault Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}
}
【3.5】第5步-调用InitializingBean#afterPropertiesSet()方法
调用InitializingBean#afterPropertiesSet()方法,执行bean初始化逻辑(若当前bean实现InitializingBean接口时,才调用,否则不调用);
应用场景: 如加载缓存;
补充: 重写 afterPropertiesSet()方法,显得spring对业务bean有侵入性, 耦合度比 xml配置文件initMetho属性指定初始化方法要高;
【3.6】第6步-调用xml配置文件initMethod属性指定的初始化方法
<!-- 配置bean的 init-method方法 --><bean id="initMethodInitializingCache" class="com.tom.springnote.chapter04.t0404beanlifecycle.initmethod.InitMethodInitializingCache"init-method="init"><constructor-arg ref="cacheService" /></bean><bean id="cacheService" class="com.tom.springnote.chapter04.t0404beanlifecycle.initmethod.CacheService" />
【3.7】第7步-BeanPostProcessor后置处理
调用 BeanPostProcessor#postProcessAfterInitialization 执行后置处理;
可以把 BeanPostProcessor#postProcessBeforeInitialization方法和postProcessAfterInitialization方法看做是 对bean初始化逻辑实现面向切面编程的方式;
【3.8】第8步:注册bean销毁前的回调方法
检查是否实现 DisposableBean 或者是否配置 destroy-method属性;若有,则注册对象销毁回调;
应用场景: 线程池对象销毁前,需要事先关闭数据库连接;
【4】bean销毁前执行回调
bean销毁:指的是spring容器停止或重启,那注册到spirng容器的bean都会被销毁;
应用场景: 如数据库连接池关闭连接;
【4.1】销毁前执行回调第1步: 调用 DisposableBean#destroy()方法;
若bean实现了DisposableBean接口才会调用destroy方法;
【4.2】销毁前执行回调第2步:调用xml配置中destroy-method指定的方法
若xml配置中bean元素的destroy-method属性有值,则调用destroy-method属性指定的方法;
<bean id="dbConnectionPool" class="com.tom.springnote.chapter04.t0404beanlifecycle.beandestory.DBConnectionPool"destroy-method="closeConnection" init-method="init"><property name="jdbcService" ref="jdbcService"/><property name="password" value="password123"/></bean>
【5】springbean实例化案例代码-创建简单版数据库连接池
【案例场景】创建简单版数据库连接池:
- 数据库连接池属性:通过setter方法注入密文密码;
- 其中properties文件配置的是密文,还需要解密为明文(BeanPostProcessor#前置处理来实现);
- spring容器停止或重启前关闭数据库连接;(注册bean销毁前回调方法 )
补充: 所有业务bean都通过spring注册,bean之间的依赖关系都通过spring绑定;
【beans0404wholelifecycle.xml】 springbean xml配置文件
<?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"><!-- 注册密码解密的BeanPostProcessor --><bean id="passwordDecodeBeanPostProcessor"class="com.tom.springnote.chapter04.t0404beanlifecycle.customBeanPostProcessor.PasswordDecodeBeanPostProcessor"></bean><!-- 配置bean的 init-method方法 --><!-- 配置bean的 destroy-method方法 --><!-- 注册 DBPasswordManager, 实现PasswordDecodable接口,依赖passwordDecodeBeanPostProcessor解密 --><bean id="dbConnectionPool" class="com.tom.springnote.chapter04.t0404beanlifecycle.beandestory.DBConnectionPool"destroy-method="closeConnection" init-method="init"><property name="jdbcService" ref="jdbcService"/><property name="password" value="encryptedPasswordText"/></bean><bean id="jdbcService" class="com.tom.springnote.chapter04.t0404beanlifecycle.beandestory.JdbcService"/>
</beans>
【BeanWholeLifecycleMain】入口
public class BeanWholeLifecycleMain {public static void main(String[] args) {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans0404wholelifecycle.xml");// 若需要注册bean销毁前回调方法,必须执行registerShutdownHook()进行注册context.registerShutdownHook();DBConnectionPool dbConnectionPool = context.getBean("dbConnectionPool", DBConnectionPool.class);System.out.println(dbConnectionPool.getPassword());System.out.println("容器关闭");}
}
【DBConnectionPool】自定义连接池
public class DBConnectionPool implements InitializingBean, DisposableBean, PasswordDecodable, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware {private JdbcService jdbcService;private String password;public DBConnectionPool() {System.out.println("DBConnectionPool:构造器");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("DBConnectionPool: InitializingBean#afterPropertiesSet()-建立与mysql连接");jdbcService.createConnection();}public void init() {System.out.println("DBConnectionPool: init-method#init()-建立与mysql连接");jdbcService.createConnection();}public void closeConnection() {System.out.println("DBConnectionPool: destory-method#closeConnection()-关闭数据库连接");jdbcService.createConnection();}@Overridepublic void destroy() throws Exception {System.out.println("DBConnectionPool: DisposableBean#destroy()-关闭数据库连接");jdbcService.createConnection();}public void setJdbcService(JdbcService jdbcService) {System.out.println("DBConnectionPool: 调用setJdbcService()");this.jdbcService = jdbcService;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("DBConnectionPool: ApplicationContextAware#setApplicationContext()");}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {System.out.println("DBConnectionPool: ApplicationEventPublisherAware#setApplicationEventPublisher()");}@Overridepublic void setMessageSource(MessageSource messageSource) {System.out.println("DBConnectionPool: MessageSourceAware#setMessageSource()");}@Overridepublic void setResourceLoader(ResourceLoader resourceLoader) {System.out.println("DBConnectionPool: ResourceLoaderAware#setResourceLoader()");}@Overridepublic String getPassword() {return password;}@Overridepublic void setDecodedPassword(String password) {this.password = password;}public void setPassword(String password) {this.password = password;}
}
【JdbcService】模拟jdbc驱动api
public class JdbcService {public void createConnection() {// do nothing.}public void closeConnection() {// do nothing.}
}
【PasswordDecodeBeanPostProcessor】 使用BeanPostProcessor#前置方法对密文密码解密
/*** @author Tom* @version 1.0.0* @ClassName PasswordDecodePostProcessor.java* @Description 密码解密bean后置处理器* @createTime 2024年08月04日 21:09:00*/
public class PasswordDecodeBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof PasswordDecodable) {System.out.println("PasswordDecodable对象的BeanPostProcessor 前置处理");PasswordDecodable passwordDecodable = (PasswordDecodable) bean;// 密文密码解密后,替换为明文passwordDecodable.setDecodedPassword(decode(passwordDecodable.getPassword()));}return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}private String decode(String cipherText) {System.out.println("PasswordDecodeBeanPostProcessor 执行解密逻辑");// 解密逻辑return "解密后" + cipherText;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean instanceof PasswordDecodable) {System.out.println("PasswordDecodable对象的BeanPostProcessor 后置处理");}return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}
}
【PasswordDecodable】密码解密接口
/*** @author Tom* @version 1.0.0* @ClassName PasswordDecodable.java* @Description 可解密的密码接口* @createTime 2024年08月04日 21:05:00*/
public interface PasswordDecodable {String getPassword();void setDecodedPassword(String password);
}
【spring构建应用系统的启动日志】
DBConnectionPool:构造器
DBConnectionPool: 调用setJdbcService()
DBConnectionPool: ResourceLoaderAware#setResourceLoader()
DBConnectionPool: ApplicationEventPublisherAware#setApplicationEventPublisher()
DBConnectionPool: MessageSourceAware#setMessageSource()
DBConnectionPool: ApplicationContextAware#setApplicationContext()
PasswordDecodable对象的BeanPostProcessor 前置处理
PasswordDecodeBeanPostProcessor 执行解密逻辑
DBConnectionPool: InitializingBean#afterPropertiesSet()-建立与mysql连接
DBConnectionPool: init-method#init()-建立与mysql连接
PasswordDecodable对象的BeanPostProcessor 后置处理
解密后的encryptedPasswordText
容器关闭
DBConnectionPool: DisposableBean#destroy()-关闭数据库连接
DBConnectionPool: destory-method#closeConnection()-关闭数据库连接