文章目录
- 【README】
- 【1】spring构建应用系统分2个阶段
- 【1.1】spring容器启动阶段
- 【1.2】springbean实例化阶段
 
- 【2】springbean生命周期概述
- 【3】springbean生命周期过程
- 【3.1】**第1步-实例化bean对象**
- 【3.2】**第2步-设置对象属性**
- 【3.3】 第3步-检查Aware接口并设置相关依赖
- 【3.4】第4步-BeanPostProcessor前置处理
- 【3.5】第5步-调用InitializingBean#afterPropertiesSet()方法
- 【3.6】第6步-调用xml配置文件initMethod属性指定的初始化方法
- 【3.7】第7步-BeanPostProcessor后置处理
- 【3.8】第8步:注册bean销毁前的回调方法
 
- 【4】bean销毁前执行回调
- 【4.1】销毁前执行回调第1步: 调用 DisposableBean#destroy()方法;
- 【4.2】销毁前执行回调第2步:调用xml配置中destroy-method指定的方法
 
- 【5】springbean实例化案例代码-创建简单版数据库连接池
【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()-关闭数据库连接



















