spring02-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 {@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实例化案例代码-创建简单版数据库连接池

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

  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:构造器");}@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()-关闭数据库连接

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单播---广播---组播

单播 单播&#xff08;Unicast&#xff09;是一种网络通信方式&#xff0c;其中数据包被发送到特定的网络接口。与广播&#xff08;Broadcast&#xff09;不同&#xff0c;单播只将数据包发送到目标地址指定的单个接收者。 单播的工作原理&#xff1a; 源地址&#xff1a;发…

DATAX自定义KafkaWriter

因为datax目前不支持写入数据到kafka中&#xff0c;因此本文主要介绍如何基于DataX自定义KafkaWriter&#xff0c;用来同步数据到kafka中。本文偏向实战&#xff0c;datax插件开发理论宝典请参考官方文档&#xff1a; https://github.com/alibaba/DataX/blob/master/dataxPlug…

240810-Gradio通过HTML组件打开本地文件+防止网页跳转到about:blank

A. 最终效果 B. 可通过鼠标点击打开文件&#xff0c;但会跳转到about:blank import gradio as gr import subprocessdef open_pptx():pptx_path /Users/liuguokai/Downloads/240528-工业大模型1.pptxtry:subprocess.Popen([open, pptx_path])return "PPTX file opened s…

【npm】如何将开发的vite插件发布到npm

前言 简单说下 npm 是什么&#xff1a; npm 是一个 node 模块管理工具&#xff0c;也是全球最大的共享源。 npm 工具与 nodejs 配套发布&#xff0c;便利开发人员共享代码。npm 主要包括 npm 官方网站、CLI&#xff08;控制台命令行工具&#xff09;、和 registry&#xff08;…

Python酷库之旅-第三方库Pandas(079)

目录 一、用法精讲 326、pandas.Series.str.normalize方法 326-1、语法 326-2、参数 326-3、功能 326-4、返回值 326-5、说明 326-6、用法 326-6-1、数据准备 326-6-2、代码示例 326-6-3、结果输出 327、pandas.Series.str.pad方法 327-1、语法 327-2、参数 327…

升级软文发稿开源系统源码论文期刊一键发布

升级软文发稿运营管理源码—论文期刊一键发布 软文发稿系统源码&#xff08;软文发布系统&#xff09;在基于旧版本的媒介软文发布平台项目改造升级了新的功能模块简称&#xff08;3.0版&#xff09;本系统还是基于开源的PHPMYSQLlayui&#xff08;前端界面&#xff09;代码进行…

Vue3使用ECharts的曲线条形堆叠混合图

先上效果图 图表容器 <div id"leftChart" style"height: 28vh"></div> <div id"rightChart" style"height: 28vh"></div> 监听resize视图窗口大小&#xff0c;可以让chart图表自适应大小 const leftChart …

wireshark使用介绍及案例分享

一、wireshark介绍 1、定义 wireshark是非常流行的网络封包分析软件,简称小鲨鱼,功能十分强大。可以截取各种网络封包,显示网络封包的详细信息。对应的,linux下的抓包工具是 tcpdump。 1.1、网络基础 参考TCP/IP五层模型,帧结构如下: 帧字段 帧字段含义 Frame 物理层的…

统计学第3天

P值 P值是原假设&#xff08;零假设&#xff09;H0为真的前提下&#xff0c;观察到的异常数据出现的概率。 如果P值很小&#xff0c;意味着原假设为真的情况下&#xff0c;取出能拒绝原假设数据的概率极低&#xff0c;此时取出了一个数据和原假设不符&#xff0c;说明了该组数…

ICMAN水位接近式检测方案(非接触式)

ICMAN水位液位接近式检测方案&#xff08;非接触式&#xff09; 我们的很多家用电器都会需要&#xff1a;液位检测 缺水&溢水提醒保护、高低液位提醒 液位传感器 像健康家电——烧水煮茶熬养生汤的烧水壶、豆浆机、养生壶等需要缺水保护和防溢液提醒&#xff1b; 像清洁…

DAMA学习笔记(十五)-数据管理组织与角色期望

1.引言 随着数据领域的快速发展&#xff0c;组织需要改进管理和治理数据的方式。当前&#xff0c;大多数组织正面临着越来越多的数据。这些数据格式多样、数量 庞大&#xff0c;并来源于不同的渠道。由于数据数量和种类的增加&#xff0c;加剧了数据 管理的复杂性。与此同时&am…

科研绘图系列:R语言多分组箱线图(grouped boxplot)

介绍 分组箱线图(Grouped Boxplot)是一种用于展示不同组别数据分布情况的统计图表。它将箱线图(Boxplot)按照不同的类别或组别进行分组,使得可以同时比较多个组别的数据特征。 箱线图本身是一种标准化的显示数据分布的方法,它能够展示数据的中位数、四分位数以及异常值…

【upload]-ini-[SUCTF 2019]CheckIn-笔记

上传图片木马文件后看到&#xff0c;检查的文件内容&#xff0c;包含<? 一句话木马提示 检查的文件格式 用如下图片木马&#xff0c;加上GIF89a绕过图片和<?检查 GIF89a <script languagephp>eval($_POST[cmd])</script> .user.ini实际上就是一个可以由用…

RAG与LLM原理及实践(11)--- Milvus hybrid search 源码分析及思想

目录 背景 hybrid search 源码分析 WeightedRanker 源码 hybrid search 核心 参数详解 基本入参 扩展入参 aysnc方式代码调用案例 说明 源码逻辑 prepare 调用过程 stub 调用结果 stub 调用过程 blocking 与 async 调用方式 深入内部core weightedRanker 的ch…

UCOSIII事件标志组详解

UCOSIII中的事件标志组是一种用于任务同步和事件管理的机制&#xff0c;它允许任务和中断服务例程&#xff08;ISR&#xff09;发布事件标志&#xff0c;并允许任务等待这些事件标志的发生。以下是对UCOSIII事件标志组的详细介绍&#xff1a; 1. 定义与创建 定义&#xff1a;…

软考:软件设计师 — 13.数据结构

十三. 数据结构 数据结构部分也可参考文章&#xff1a;Java数据结构知识点 — 5种常见数据结构 1. 线性结构 &#xff08;1&#xff09;线性表 顺序表 线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的数据元素&#xff0c;从而使得逻辑上相邻的两个元素…

并行计算模型

像其他专业行话一样&#xff0c;并行计算也有自己的行话。行话就像个大坑&#xff0c;坑中的人需要在其中浸淫很久&#xff0c;才能逐渐适应其语境&#xff0c;然而很多行话的使用常常是草率与不精确的。有时候把鬼都听不懂的行话理解了&#xff0c;再跟别人说鬼话&#xff0c;…

【MySQL 06】表的约束

文章目录 &#x1f308; 一、约束的概念&#x1f308; 二、空属性约束⭐ 1. 空值无法参与运算⭐ 2. 设置非空属性 &#x1f308; 三、默认值约束⭐ 1. 默认值使用案例⭐ 2. 同时设置 not null 和 default &#x1f308; 四、列描述约束&#x1f308; 五、zerofill 补零约束&…

校园外卖平台小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商家管理&#xff0c;菜品信息管理&#xff0c;菜品分类管理&#xff0c;购买菜品管理&#xff0c;订单信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&a…

【python报错已解决】`IndexError: list index out of range`

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 引言一、问题描述1.1 报错示例1.2 报错分析1.3 解决思路 二、解决方法2.1 方法一&#xff1a;检查索引范围2.2 方法二…