spring揭秘02-springbean生命周期(实例化过程)

news2024/9/22 11:37:22

文章目录

  • 【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生命周期概述

  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()-关闭数据库连接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2042142.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入理解计算机系统 CSAPP 实验lab:Attack Lab

资源下载地址:CS:APP3e, Bryant and OHallaron CTARGET objdump -d ctarget >ctarget.txt 反汇编我们下载的ctarget打开看看 touch1: 我们期望的是getbuf执行完之后直接跳到touch1,利用缓存区溢出达到找个目的. 汇编显示call Gets之前 sub $0x28,%rsp,栈指针减小了 0x28 …

JMeter高效管理测试数据-参数化

文章目录 1.什么是参数化2.定义变量3.CSV数据文件设置 1.什么是参数化 在JMeter中&#xff0c;参数化是一种常用的技术&#xff0c;用于使测试场景更加灵活和动态。通过参数化&#xff0c;你可以让JMeter在每次请求中使用不同的值&#xff0c;这在模拟真实用户行为或测试不同输…

【mybatis-plus@Select注解联表查询实现分页】

前言 MybatisPlus对于单表的操作很方便&#xff0c;但是多表查询等复杂的操作还是需要在xml中写sql语句来完成。那么&#xff0c;在MybatisPlus中如何实现多表联查、分页查询呢。 一、数据库表设计 我们模拟一个购物的情况&#xff0c;在数据库创建两个表&#xff0c;一个用户…

MeterSphere接口测试脚本断言

MeterSphere接口测试脚本断言 我们在接口自动化测试过程中&#xff0c;经常遇到无论我们传入什么数据信息&#xff0c;只要响应体报文中某个字段为不固定的特定信息&#xff08;如&#xff1a;或1或2或3&#xff09;&#xff0c;就符合预期&#xff0c;流程就可以继续&#xf…

通过JPA映射数据库

在 JPA (Java Persistence API) 中&#xff0c;实体类通常通过一系列注解来配置&#xff0c;以便与数据库表进行映射,与Mybatis-plus在功能上&#xff0c;具有一定的相似性,都是持久化框架。 package com.plumcarefree.demo.entity.user; import javax.persistence.*; impor…

用Python爬取高德地图地铁站点数据——Python程序及详解

0. 准备工作 在使用以下Python程序爬取高德地图地铁站点数据前&#xff0c;需要先在“高德开放平台”&#xff08;网站&#xff1a;高德开放平台 | 高德地图API&#xff09;申请一个API Key。具体操作为&#xff1a;注册一个“高德开放平台”账号&#xff0c;找到右上角的控制…

ImportError: DLL load failed: 找不到指定的程序的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

mycat读写分离实现、企业架构MySQL读写分离

一、mycat读写分离实现 1. 添加一个新的虚拟主机&#xff0c;设置ip为10.1.1.60,主机名为mycat.yuanyu.zhangmin.关闭防火墙 SELinux NetworkManager 2. 上传jdk和mycat安装包 3. 解压并且添加到指定的位置 [rootmycat ~]# ls anaconda-ks.cfg frp initserver.sh jdk1.8.0_19…

24暑假算法刷题 | Day30 | 贪心算法 IV | LeetCode 452. 用最少数量的箭引爆气球,435. 无重叠区间,763. 划分字母区间

目录 452. 用最少数量的箭引爆气球题目描述题解 435. 无重叠区间题目描述题解 763. 划分字母区间题目描述题解 452. 用最少数量的箭引爆气球 点此跳转题目链接 题目描述 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0c;其中…

【Docker】Elasticsearch 8.12 安装与搭建

由于工作过程中硬件资源未能达到要求&#xff0c;现将使用 Elasticsearch 取代原 Redis MySQL Milvus 技术栈&#xff0c;在重新搭建的同时记录一下搭建过程&#xff08;由于之前也分享过 Docker 版本 Elasticsearch 5.x 和 Elasticsearch 7.x 的部署方法&#xff0c;因此本文…

横看成岭侧成峰,远近高低各不同 —— 深入探讨Obfs4流量识别:特征、方法与应用

目录 引言 Obfs4流量特征 数据包大小与分布 时间间隔&#xff08;IAT&#xff09; 流量方向 加密特征 Obfs4流量识别方法 深度学习模型识别 示例代码 基于部分数据的多级剪枝方法 方法步骤 深度包检测与机器学习结合 应用场景 网络安全监控 Tor网络访问优化 匿…

分层神经网络(DNN)知多少?

分层神经网络作为深度学习的核心架构&#xff0c;已经彻底改变了我们处理复杂问题的方式。本文将带我们走进神经网络的基础知识&#xff0c;揭开GPT这些强大模型的神秘面纱。希望通过本文的介绍&#xff0c;我们不仅能够理解神经网络的基本构成&#xff0c;还能洞察到它们是如何…

OpenCV图像处理——轮廓的面积与弧长计算(C++/Python)

概述 轮廓面积与轮廓周长是图像分析中的两项核心统计特征&#xff0c;它们为理解和量化图像中的形状提供了基础。 轮廓面积&#xff1a;这代表了轮廓所界定区域的像素数量&#xff0c;是衡量区域大小的直接指标。面积的计算结果以像素平方为单位&#xff0c;为我们提供了一个量…

Wireshark_DNS_v7.0

Wireshark_DNS_v7.0 一、 nslookup 前置 nslookup 是一个网络命令行工具&#xff0c;用于查询域名系统&#xff08;DNS&#xff09;中的域名解析记录。通过使用 nslookup&#xff0c;你可以获取某个域名的IP地址&#xff0c;或者获取与某个IP地址关联的域名信息。 查看域名…

学校医院NTP电子钟让时间管理更加智能

在学校和医院这样的重要场所&#xff0c;时间的精确管理至关重要。每一分每一秒都可能关系到教学的进度、医疗的效果以及师生和患者的体验。而 NTP 电子钟的出现&#xff0c;为学校和医院的时间管理带来了全新的智能化变革。 一、NTP 电子钟在学校应用 NTP 电子钟&#xff0c;…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第五篇 文件系统构建篇-第七十八章 离线构建Yocto系统

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

【HarmonyOS】鸿蒙应用蓝牙功能实现 (一)

【HarmonyOS】鸿蒙应用蓝牙功能实现 前言 蓝牙技术是一种无线通信技术&#xff0c;可以在短距离内传输数据。它是由爱立信公司于1994年提出的&#xff0c;使用2.4 GHz的ISM频段&#xff0c;可以在10米左右的距离内进行通信。可以用于连接手机、耳机、音箱、键盘、鼠标、打印机…

工厂流水线MES报工一体机改善生产管理效率

作为智能制造的重要组成部分&#xff0c;MES系统&#xff08;制造执行系统&#xff09;在优化生产流程、提高生产效率、降低生产成本等方面发挥着不可替代的作用。而MES报工一体机作为MES系统的重要组成部分&#xff0c;更是帮助企业实现生产管理效率提升的利器。 一、MES报工一…

为何有了云计算,还需要边缘计算?EasyCVR视频平台助力数据高效汇聚与管理

在当今数字化的时代&#xff0c;云计算可谓是大名鼎鼎&#xff0c;它为我们的生活和工作带来了巨大的便利。但你有没有想过&#xff0c;既然有了强大的云计算&#xff0c;为什么还会出现边缘计算呢&#xff1f; 一、云计算与边缘计算&#xff1a;相辅相成的科技双雄 先来说说…

船员考证题库刷题

1、船舶主配电板系统&#xff0c;设有地气灯、配电板式兆欧表和低绝缘报警装置&#xff0c;及船舶照明分配电箱&#xff0c;当发生单相接地故障时而进行排查时&#xff0c;______设备是不能随意断电的。 A、厨房用电 B、卫星基站 C、起重机用电 答案&#xff1a;B 2、燃油…