文章目录
【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 {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default 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:构造器");
}
@Override
public 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();
}
@Override
public 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;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("DBConnectionPool: ApplicationContextAware#setApplicationContext()");
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
System.out.println("DBConnectionPool: ApplicationEventPublisherAware#setApplicationEventPublisher()");
}
@Override
public void setMessageSource(MessageSource messageSource) {
System.out.println("DBConnectionPool: MessageSourceAware#setMessageSource()");
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
System.out.println("DBConnectionPool: ResourceLoaderAware#setResourceLoader()");
}
@Override
public String getPassword() {
return password;
}
@Override
public 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 {
@Override
public 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;
}
@Override
public 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()-关闭数据库连接