Spring源码分析---Bean 的生命周期 03

来源:Spring

3. Bean 的生命周期

自定义一个 SpringBoot 的主启动类:

@SpringBootApplication
public class A03Application {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(A03Application.class, args);// 调用 close 方法,显示生命周期的销毁阶段context.close();}
}

在启动类所在路径下再定义一个类,使其能够被自动装配:

@Slf4j
@Component
public class LifeCycleBean {public LifeCycleBean() {log.info("构造");}@Autowiredpublic void autowire(@Value("${JAVA_HOME}") String home) {log.info("依赖注入: {}", home);}@PostConstructpublic void init() {log.info("初始化");}@PreDestroypublic void destroy() {log.info("销毁");}
}

运行主启动类,查看控制台的日志信息(只列举主要信息):

test.bean.a03.LifeCycleBean        : 构造
test.bean.a03.LifeCycleBean        : 依赖注入: D:\environment\JDK1.8
test.bean.a03.LifeCycleBean        : 初始化
test.bean.a03.LifeCycleBean        : 销毁

除此之外,Spring 还提供了一些对 Bean 生命周期的各个阶段进行拓展的 BeanPostProcessor,比如 InstantiationAwareBeanPostProcessor 和 DestructionAwareBeanPostProcessor。

实现这两个接口,并使用 @Component 注解标记实现类:

@Slf4j
@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {@Overridepublic void postProcessBeforeDestruction(Object o, String beanName) throws BeansException {if ("lifeCycleBean".equals(beanName)) {log.info("<<<<<<<<<< 销毁执行之前,如 @PreDestroy");}}@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {if ("lifeCycleBean".equals(beanName)) {log.info("<<<<<<<<<< 实例化之前执行,这里返回的对象会替换掉原本的 bean");}return null;}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if ("lifeCycleBean".equals(beanName)) {log.info("<<<<<<<<<< 实例化之后执行,如果返回 false 会跳过依赖注入节点");// return false;}return true;}@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {if ("lifeCycleBean".equals(beanName)) {log.info("<<<<<<<<<< 依赖注入阶段执行,如 @Autowired、@Value、@Resource");}return pvs;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if ("lifeCycleBean".equals(beanName)) {log.info("<<<<<<<<<< 初始化执行之前,这里返回的对象会替换掉原本的 bean,如 @PostConstruct、@ConfigurationProperties");}return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if ("lifeCycleBean".equals(beanName)) {log.info("<<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的 bean,如代理增强");}return bean;}
}

再运行主启动类,查看控制台的日志信息(只列举主要信息):

test.bean.a03.MyBeanPostProcessor  : <<<<<<<<<< 实例化之前执行,这里返回的对象会替换掉原本的 bean
test.bean.a03.LifeCycleBean        : 构造
test.bean.a03.MyBeanPostProcessor  : <<<<<<<<<< 实例化之后执行,如果返回 false 会跳过依赖注入节点
test.bean.a03.MyBeanPostProcessor  : <<<<<<<<<< 依赖注入阶段执行,如 @Autowired@Value@Resource
test.bean.a03.LifeCycleBean        : 依赖注入: D:\java\JDK1.8.30
test.bean.a03.MyBeanPostProcessor  : <<<<<<<<<< 初始化执行之前,这里返回的对象会替换掉原本的 bean,如 @PostConstruct@ConfigurationProperties
test.bean.a03.LifeCycleBean        : 初始化
test.bean.a03.MyBeanPostProcessor  : <<<<<<<<<< 初始化之后执行,这里返回的对象会替换掉原本的 bean,如代理增强
test.bean.a03.MyBeanPostProcessor  : <<<<<<<<<< 销毁执行之前,如 @PreDestroy
test.bean.a03.LifeCycleBean        : 销毁

为什么实现了 BeanPostProcessor 接口后就能够在 Bean 生命周期的各个阶段进行拓展呢?

这使用了模板方法设计模式。

现有如下代码,模拟 BeanFactory 构造 Bean:

static class MyBeanFactory {public Object getBean() {Object bean = new Object();System.out.println("构造 " + bean);System.out.println("依赖注入 " + bean);System.out.println("初始化 " + bean);return bean;}
}

假设现在需要在依赖注入之后,初始化之前进行其他的操作,那首先能想到的就是在这个位置直接书写相关操作的代码,但这会使代码更加臃肿、增加耦合性,显然不是一种好方式。

可以定义一个接口:

interface BeanPostProcessor {void inject(Object bean);
}

然后对 MyBeanFactory 进行修改:

static class MyBeanFactory {public Object getBean() {Object bean = new Object();System.out.println("构造 " + bean);System.out.println("依赖注入 " + bean);for (BeanPostProcessor processor : processors) {processor.inject(bean);}System.out.println("初始化 " + bean);return bean;}private List<BeanPostProcessor> processors = new ArrayList<>();public void addProcessor(BeanPostProcessor processor) {processors.add(processor);}
}

之后如果需要拓展,调用 MyBeanFactory 实例的 addProcessor() 方法添加拓展逻辑即可:

public static void main(String[] args) {MyBeanFactory beanFactory = new MyBeanFactory();beanFactory.addProcessor(bean -> System.out.println("解析 @Autowired"));beanFactory.addProcessor(bean -> System.out.println("解析 @Resource"));beanFactory.getBean();
}
构造 java.lang.Object@49097b5d
依赖注入 java.lang.Object@49097b5d
解析 @Autowired
解析 @Resource
初始化 java.lang.Object@49097b5d

Bean 生命周期图:
在这里插入图片描述

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

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

相关文章

选择免费的SSL证书,还是付费的?

作为一个互联网文章作者&#xff0c;我会根据具体的使用场景和需求来选择SSL证书。通常情况下&#xff0c;如果是用于个人网站或者小型项目&#xff0c;我会倾向于选择免费的SSL证书&#xff0c;比如 JoySSL提供的免费证书。这样可以在不增加额外费用的情况下为网站提供安全的加…

静态HTTP与CDN:如何优化内容分发

大家好&#xff0c;今天我们来聊聊静态HTTP和CDN这对“黄金搭档”。没错&#xff0c;就是那个让你的网站内容像闪电一样传遍全球的CDN&#xff01; 首先&#xff0c;我们来了解一下静态HTTP。它就像是那个老实可靠的邮差&#xff0c;每次都按时按点地把你的内容送到用户手中。…

第二十一章博客

计算机应用实现了多台计算机间的互联&#xff0c;使得它们彼此之间能够进行数据交流。网络应用程序就是在已连接的不同计算机上运行的程序&#xff0c;这些程序借助于网络协议&#xff0c;相互之间可以交换数据。编写网络应用程序前&#xff0c;首先必须明确所要使用的网络协议…

C++ boost planner_cond_.wait(lock) 报错1225

1.如下程序段 boost unique_lock doesn’t own the mutex: Operation not permitted 问题&#xff1a; 其中makePlan是一个线程。这里的unlock导致错误这个报错 boost unique_lock doesn’t own the mutex: Operation not permitted bool navigation::makePlan(){ //cv::named…

MySQL中如何快速定位占用CPU过高的SQL

作为DBA工作中都会遇到过数据库服务器CPU飙升的场景&#xff0c;我们该如何快速定位问题&#xff1f;又该如何快速找到具体是哪个SQL引发的CPU异常呢&#xff1f;下面我们说两个方法。聊聊MySQL中如何快速定位占用CPU过高的SQL。 技术人人都可以磨炼&#xff0c;但处理问题的思…

华为OD机试 - 多段线数据压缩(Java JS Python C)

在线OJ刷题 题目详情 - 多段线数据压缩 - Hydro 题目描述 下图中,每个方块代表一个像素,每个像素用其行号和列号表示。 为简化处理,多线段的走向只能是水平、竖直、斜向45度。 上图中的多线段可以用下面的坐标串表示:(2,8),(3,7),(3,6),(3,5),(4,4),(5,3),(6,2),(7,3),(…

042、序列模型

之——从时序中获取信息 目录 之——从时序中获取信息 杂谈 正文 1.建模 2.方案A-马尔科夫假设 3.方案B-潜变量模型 4.简单实现 杂谈 很多连续的数据都是有前后的时间相关性的&#xff0c;并不是每一个单独的数据是随机出现的。在时序中会蕴含一些空间结构的变化信息、…

【数据科学】一文彻底理清数据、数据类型、数据结构的概念

一、什么是数据&#xff1f; 入门数据学科&#xff0c;首先第一步要认识数据什么&#xff0c;可能大多数人都无法对数据做一个准确的定义&#xff0c;在我们印象中&#xff0c;提到数据首先头脑浮现的是数据表格&#xff0c;是一堆堆数字&#xff0c;那么数据就是数字吗&#x…

SpringBoot 2.0 中默认 HikariCP 数据库连接池原理解析

作为后台服务开发&#xff0c;在日常工作中我们天天都在跟数据库打交道&#xff0c;一直在进行各种CRUD操作&#xff0c;都会使用到数据库连接池。按照发展历程&#xff0c;业界知名的数据库连接池有以下几种&#xff1a;c3p0、DBCP、Tomcat JDBC Connection Pool、Druid 等&am…

阿里云服务器记录

阿里云服务器记录 CentOS 8.4 64位 SCC版 CentOS 7.9 64位 SCC版 CentOS 7.9 64位 CentOS 7.9 64位 UEFI版 Alibaba Cloud Linux Anolis OS CentOS Windows Server Ubuntu Debian Fedora OpenSUSE Rocky Linux CentOS Stream AlmaLinux 阿里云服务器有个scc版&#xff0c;这个…

Flask+Mysql项目docker-compose部署(Pythondocker-compose详细步骤)

一、前言 环境&#xff1a; Linux、docker、docker-compose、python(Flask)、Mysql 简介&#xff1a; 简单使用Flask框架写的查询Mysql数据接口&#xff0c;使用docker部署&#xff0c;shell脚本启动 优势&#xff1a; 采用docker方式部署更加便于维护&#xff0c;更加简单快…

“C语言“——scanf()、getchar() 、putchar()、之间的关系

scanf函数说明 scanf函数是对来自于标准输入流的输入数据作格式转换&#xff0c;并将转换结果保存至format后面的实参所指向的对象。 而const char*format 指向的字符串为格式控制字符串&#xff0c;它指定了可输入的字符串以及赋值时转换方法。 简单来说给一个打印格式(输入…

【并发编程篇】源码分析,手动创建线程池

文章目录 &#x1f6f8;前言&#x1f339;Executors的三大方法 &#x1f354;简述线程池&#x1f386;手动创建线程池⭐源码分析✨代码实现&#xff0c;手动创建线程池&#x1f388;CallerRunsPolicy()&#x1f388;AbortPolicy()&#x1f388;DiscardPolicy()&#x1f388;Dis…

LNPMariadb数据库分离|web服务器集群

LNP&Mariadb数据库分离&#xff5c;web服务器集群 网站架构演变单机版LNMP独立数据库服务器web服务器集群与Session保持 LNP与数据库分离1. 准备一台独立的服务器&#xff0c;安装数据库软件包2. 将之前的LNMP网站中的数据库迁移到新的数据库服务器3. 修改wordpress网站配置…

2023.12.24 关于 Redis 中 String 类型内部编码 及 应用场景

目录 String 类型内部编码 3 种内部编码方式 String 类型应用场景 Cache 缓存 键名命名规则 计数&#xff08;Counter&#xff09; 共享会话&#xff08;Session &#xff09; 手机验证码 总结 String 类型内部编码 3 种内部编码方式 int&#xff1a;用来表示 64 位 —…

小天使的小难题:新生儿疝气的关注与温馨呵护

引言&#xff1a; 新生儿疝气是一种在出生后可能出现的常见情况&#xff0c;虽然通常不会造成长期影响&#xff0c;但对于家长而言&#xff0c;了解如何正确应对新生儿疝气是至关重要的。本文将深入探讨新生儿疝气的原因、症状&#xff0c;以及家长在面对这一问题时应该采取的…

1224. 交换瓶子(蓝桥杯/图论)

题目&#xff1a; 1224. 交换瓶子 - AcWing题库 输入样例1&#xff1a; 5 3 1 2 5 4输出样例1&#xff1a; 3输入样例2&#xff1a; 5 5 4 3 2 1输出样例2&#xff1a; 2 思路&#xff1a;图论 1.将对应的位置与当前的瓶子序列相连形成环。 2.最少交换次数能形成的最多…

【【迭代16次的CORDIC算法-verilog实现】】

迭代16次的CORDIC算法-verilog实现 -32位迭代16次verilog代码实现 CORDIC.v module cordic32#(parameter DATA_WIDTH 8d32 , // we set data widthparameter PIPELINE 5d16 // Optimize waveform)(input …

第十四章Java博客

lambda就是数学中的“λ”的读音&#xff0c;lambda表达式是基于λ演算而得名的&#xff0c;因为lambda抽象&#xff08;lambda abstraction&#xff09;表示一个匿名的函数&#xff0c;于是开发语言也将lambda表达式用来表示匿名函数&#xff0c;也就是没有函数名字的函数。C#…

C语言字符串处理提取时间(ffmpeg返回的时间字符串)

【1】需求 需求&#xff1a;有一个 “00:01:33.90” 这样格式的时间字符串&#xff0c;需要将这个字符串的时间值提取打印出来&#xff08;提取时、分、秒、毫秒&#xff09;。 这个时间字符串从哪里来的&#xff1f; 是ffmpeg返回的时间&#xff0c;也就是视频的总时间。 下…