spring揭秘02-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生命周期概述

  1. 容器在启动阶段,仅仅收集了 BeanDefinition,来保存实例化阶段将要用到的必要信息;只有当请求方请求某个对象实例时,才有可能调用getBean()方法触发bean实例化;

  2. BeanFactory的getBean方法可以被客户端对象显式调用,也可以在容器内部隐式调用,隐式调用有如下两种情况:

    1. 对于BeanFactory, 对象实例化默认采用延迟初始化;即当对象被请求时,才实例化;
    2. 对于ApplicationContext,容器启动完成后会实例化所有bean;即容器启动完成后,紧接着调用getBean() 方法实例化所有bean
      1. 具体的, ApplicationContext容器启动完成后,调用refresh()方法,refresh方法再调用getBean()方法对所有对象全部实例化;
      2. 调用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接口清单如下:

  1. BeanNameAware:把beanName注入(装配)到当前对象;
  2. BeanClassLoaderAware: 把ClassLoader注入(装配)到当前对象;
  3. BeanFactoryAware: 把BeanFactory注入(装配)到当前对象;

ApplicationContext容器中bean可以实现的Aware接口清单如下:

  1. ResourceLoaderAware: 把 ResourceLoader注入(装配)到当前对象;
  2. ApplicationEventPublisherAware: 把 ApplicationEventPublisher 注入(装配)到当前对象;
  3. MessageSourceAware: 把 MessageSource注入(装配)到当前对象;
  4. ApplicationContextAware: 把ApplicationContext注入(装配)到当前对象;
  5. 补充: 因为ApplicationContext 实现了 ResourceLoader, ApplicationEventPublisher ,MessageSource接口,所以上述注入过程实际上是注入ApplicationContext本身给当前对象;可以参见 ApplicationContextAwareProcessor 这个 BeanPostProcessor 实现类;

应用场景: 使得非spring框架的bean(如业务bean)可以获取spring框架bean的引用,如获取ApplicationContext spring容器,然后通过调用ApplicationContext#get()方法获取某个bean实例;

【3.4】第4步-BeanPostProcessor前置处理

BeanPostProcessor有2个方法:

  1. postProcessBeforeInitialization: bean初始化前置处理;
  2. postProcessAfterInitialization:bean初始化后置处理;
  3. 补充:bean初始化逻辑包括 调用InitializingBean#afterPropertiesSet() 和 通过xml配置init-method方法;

BeanPostProcessor应用场景:

  1. 处理标记接口实现类
  2. 为当前对象提供代理实现;
  3. 替换当前对象实例;
  4. 字节码增强当前对象实例;
  5. SpringAOP为对象生成相应的代理对象;如 BeanNameAutoProxyCreator的BeanPostProcessor实现类;
  6. 各种资源密码加解密; 如连接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实例化案例代码-创建简单版数据库连接池

【案例场景】创建简单版数据库连接池:

  1. 数据库连接池属性:通过setter方法注入密文密码;
  2. 其中properties文件配置的是密文,还需要解密为明文(BeanPostProcessor#前置处理来实现);
  3. 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()-关闭数据库连接
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值