Spring——Spring相关类原理与实战

摘要

本文深入探讨了 Spring 框架中 InitializingBean 接口的原理与实战应用,该接口是 Spring 提供的一个生命周期接口,用于在 Bean 属性注入完成后执行初始化逻辑。文章详细介绍了接口定义、作用、典型使用场景,并与其他相关概念如 @PostConstruct 和 DisposableBean 进行了对比。

1. InitializingBean原理与实战

InitializingBean 是 Spring 提供的一个生命周期接口,其核心作用是让 Bean 在所有属性注入完成后执行一些初始化逻辑。

1.1. 🧩 接口定义

public interface InitializingBean {void afterPropertiesSet() throws Exception;
}

如果你的类实现了这个接口,Spring 会在完成依赖注入后自动调用 afterPropertiesSet() 方法。

1.2. ✅ InitializingBean作用

功能

说明

生命周期钩子

在 Bean 初始化(注入属性)后执行自定义逻辑

替代 @PostConstruct

是一种“接口驱动”的初始化方式

适用于框架开发

明确初始化点,便于统一管理

1.3. ✅ 典型使用场景(实战)

1.3.1. 初始化资源、连接等

@Component
public class RedisClient implements InitializingBean {private JedisPool jedisPool;@Value("${redis.host}")private String host;@Value("${redis.port}")private int port;@Overridepublic void afterPropertiesSet() throws Exception {jedisPool = new JedisPool(host, port);System.out.println("Redis 连接池初始化完成");}public Jedis getConnection() {return jedisPool.getResource();}
}

📌 实战说明:配置注入完成后,通过 afterPropertiesSet() 初始化 Redis 连接池。

1.3.2. 校验依赖是否注入完整

@Component
public class SomeService implements InitializingBean {@Autowiredprivate SomeDependency dependency;@Overridepublic void afterPropertiesSet() {if (dependency == null) {throw new IllegalStateException("SomeDependency 没有被注入!");}}
}

1.3.3. 在框架中设置静态访问点(如 XXL-Job 中)

@Override
public void afterPropertiesSet() throws Exception {adminConfig = this; // 设置静态访问入口xxlJobScheduler = new XxlJobScheduler();xxlJobScheduler.init(); // 初始化调度器
}

📌 实战说明:用于启动调度器、加载配置等初始化逻辑,适合中间件开发。

1.4. ✅ InitializingBean总结

  • InitializingBean 提供了一种 标准化的 Bean 初始化入口
  • 推荐用于:底层框架、组件、工具类初始化中,如连接池、调度器、配置校验。
  • 对于业务逻辑,更推荐用 @PostConstruct 注解,简洁易读。

2. @PostConstruct注解原理与实战

@PostConstruct 是 Java 提供的标准注解(来自 javax.annotationjakarta.annotation),在 Spring 中用于定义 Bean 的初始化方法,当 Bean 完成依赖注入之后自动执行。

2.1. ✅ @PostConstruct 是什么?

@PostConstruct
public void init() {// 初始化逻辑
}
  • 当 Spring 完成对 Bean 的创建与依赖注入后,会自动调用标注了 @PostConstruct 的方法。
  • 方法 只能有一个、无参数、返回值为 void

2.2. ✅ @PostConstruct与Spring生命周期的关系

Spring 创建一个 Bean 的完整流程如下:

构造函数 -> 依赖注入 -> @PostConstruct -> InitializingBean.afterPropertiesSet() -> Bean 初始化完成

因此,@PostConstruct 执行在 Bean 初始化的早期阶段,非常适合做以下操作:

用途

说明

资源初始化

建立连接池、定时器、缓存等

参数检查

校验注入的配置是否符合要求

注册操作

向注册中心、事件总线等注册自己

静态赋值

注入静态成员变量或构造辅助工具

2.3. ✅ 实战使用示例

2.3.1. 缓存初始化

@Component
public class DictCache {private final DictService dictService;private Map<String, String> dictCache = new HashMap<>();public DictCache(DictService dictService) {this.dictService = dictService;}@PostConstructpublic void loadCache() {dictCache = dictService.loadAllDict();System.out.println("字典缓存加载完毕!");}public String get(String key) {return dictCache.get(key);}
}

2.3.2. 参数校验或默认值设置

@Component
public class SmsSender {@Value("${sms.gateway.url}")private String gatewayUrl;@PostConstructpublic void validate() {if (gatewayUrl == null || gatewayUrl.isEmpty()) {throw new IllegalArgumentException("短信网关地址不能为空!");}}
}

2.3.3. 设置静态工具类

@Component
public class SpringContextUtil {@Autowiredprivate ApplicationContext applicationContext;public static ApplicationContext context;@PostConstructpublic void init() {SpringContextUtil.context = this.applicationContext;}public static <T> T getBean(Class<T> clazz) {return context.getBean(clazz);}
}

2.4. ✅ @PostConstruct与InitializingBean 的对比

特性

@PostConstruct

InitializingBean

来源

Java 标准注解

Spring 接口

侵入性

低(无需实现接口)

高(必须实现接口)

可读性

更清晰、简洁

稍显繁琐

推荐

✅ 推荐

❌ 一般不推荐用于业务代码

场景

普通业务初始化

框架、组件级初始化

3. @PostConstructInitializingBean区别?

缓存初始化 场景中,@PostConstructInitializingBean 都能完成初始化逻辑,但两者有以下核心区别

  • @PostConstruct 是注解驱动的初始化方式,简洁、解耦、推荐用于业务代码
  • InitializingBean 是接口驱动的初始化方式,侵入性强,推荐用于框架/中间件级别代码

3.1. ✅ 功能与使用上的对比

对比项

@PostConstruct

InitializingBean

本质

Java 标准注解(JSR-250)

Spring 生命周期接口

编码方式

在方法上加注解

实现接口、重写方法

方法名

任意(如 init()

固定:afterPropertiesSet()

方法个数

可以多个类中各定义一个

一个类只能有一个 afterPropertiesSet()

侵入性

✅ 低:无须继承或实现接口

❌ 高:必须实现接口

可读性

✅ 强:一看注解就知道是初始化

❌ 差:容易被忽视

可测试性

✅ 好(不会影响类结构)

❌ 接口实现影响结构

场景适用

业务代码、工具类、缓存加载

框架设计、底层组件、可复用模块

推荐程度

✅ 更推荐使用

⚠️ 更适合框架代码

3.2. ✅ 实际缓存初始化场景对比

3.2.1. 用@PostConstruct 初始化缓存

@Component
public class DictCache {@Autowiredprivate DictService dictService;private Map<String, String> cache = new HashMap<>();@PostConstructpublic void initCache() {cache = dictService.loadAllDict();}
}

优点

  • 简洁明了
  • 不影响类结构
  • 易于测试和维护

3.2.2. 用InitializingBean 初始化缓存

@Component
public class DictCache implements InitializingBean {@Autowiredprivate DictService dictService;private Map<String, String> cache = new HashMap<>();@Overridepublic void afterPropertiesSet() throws Exception {cache = dictService.loadAllDict();}
}

⚠️ 缺点

  • 需要实现接口,影响类设计
  • 方法名固定,不够语义化
  • 可读性差:不能一眼看出这是初始化方法

3.3. ✅ 实际开发推荐

场景

推荐方式

普通业务初始化(如缓存、参数)

@PostConstruct

框架开发 / 高通用组件(如连接池、调度器)

InitializingBean

初始化逻辑中涉及多个阶段、多个顺序

❌都不推荐,建议用 @Bean(initMethod = "...") 或配置类

4. DisposableBean原理与实战

DisposableBean 是 Spring 提供的一个生命周期接口,用于在 Bean 被销毁时执行清理逻辑,常用于释放资源、关闭连接、销毁线程池等操作。DisposableBean 接口的 destroy() 方法会在 Spring 容器销毁该 Bean 之前被调用,用于完成资源释放或清理工作。

4.1. 🧩 接口定义

public interface DisposableBean {void destroy() throws Exception;
}

Spring 会在 容器关闭前 调用实现类的 destroy() 方法。

4.2. ✅ 释放线程池

@Component
public class TaskManager implements DisposableBean {private ExecutorService threadPool = Executors.newFixedThreadPool(10);public void submit(Runnable task) {threadPool.submit(task);}@Overridepublic void destroy() throws Exception {System.out.println("正在关闭线程池...");threadPool.shutdown();}
}

📝 当 Spring 容器关闭时,destroy() 方法被自动调用,安全关闭线程池。

4.3. ✅ 关闭数据库连接或 Redis 客户端

java复制编辑
@Component
public class RedisClientWrapper implements DisposableBean {private JedisPool jedisPool = new JedisPool("localhost");public Jedis getClient() {return jedisPool.getResource();}@Overridepublic void destroy() throws Exception {System.out.println("关闭 Redis 连接池");jedisPool.close();}
}

4.4. ✅ 结合 InitializingBean

有时你会在一个类中同时使用初始化和销毁逻辑:

@Component
public class MyService implements InitializingBean, DisposableBean {@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("初始化资源");}@Overridepublic void destroy() throws Exception {System.out.println("清理资源");}
}

4.5. 🛠️ 替代方案:@PreDestroy

Spring 也支持使用 @PreDestroy 注解实现销毁逻辑:

@PreDestroy
public void cleanup() {System.out.println("释放资源 @PreDestroy");
}

4.5.1. 🔍 区别总结:

对比项

DisposableBean

@PreDestroy

来源

Spring 接口

Java 标准注解(JSR-250)

入侵性

高(实现接口)

低(注解)

推荐程度

❌ 较低

✅ 更推荐

方法限制

固定 destroy()

方法名可自定义

结构清晰

❌ 不利于多继承

✅ 解耦、灵活

4.6. ✅ 总结

项目

说明

接口名

DisposableBean

方法

destroy()

调用时机

Bean 被销毁前(如容器关闭)

常见用途

释放线程池、关闭连接池、清理缓存等

推荐替代

使用 @PreDestroy更灵活、非侵入式

使用场景

资源管理类、任务调度类、连接池等生命周期敏感组件

如果你在业务中需要在 Spring 应用关闭时清理资源,推荐使用 @PreDestroy;如果你正在写一个框架、组件,或者需要精确控制 Bean 生命周期,则可以使用 DisposableBean

5. SmartInitializingSingleton原理与实战

SmartInitializingSingleton 是 Spring 框架中的一个 高级扩展接口,用于在 所有单例 Bean 完全初始化完成后 执行某段逻辑。它常用于 组件启动逻辑的延迟执行,比如像 XXL-JOB 的执行器、缓存预热、动态注册。

5.1. SmartInitializingSingleton 是什么?

public interface SmartInitializingSingleton {void afterSingletonsInstantiated();
}
  • Spring 会在所有单例 Bean 实例化并依赖注入完成后(即 Spring 容器即将就绪时)调用这个接口的实现。
  • InitializingBean.afterPropertiesSet() 不同,它只调用一次,且是在所有单例准备完毕后调用。

5.2. 与InitializingBean区别?

项目

InitializingBean

SmartInitializingSingleton

调用时机

单个 Bean 初始化后

所有单例 Bean 初始化后

触发方式

每个 Bean 单独触发

容器完成所有初始化后统一触发

应用场景

Bean 自己初始化逻辑

全局依赖逻辑(如依赖其他 Bean 已完成)

替代方式

@PostConstruct

无注解替代方式(需接口或 SmartLifecycle

实现目标

Bean 自己初始化

等待所有 Bean 初始化再统一处理

5.3. 实战场景和原理解析

5.3.1. 🌟 场景一:自动注册任务(如 XXL-JOB)

@Component
public class XxlJobSpringExecutor extends XxlJobExecutor
implements SmartInitializingSingleton {@Overridepublic void afterSingletonsInstantiated() {initJobHandlerMethodRepository(applicationContext); // 扫描 @XxlJobsuper.start(); // 启动注册}
}

📌 原因:只有当所有 Bean 都加载完,才能扫描所有 Bean 中的方法。

5.3.2. 🌟 场景二:事件总线初始化(如 Guava EventBus)

@Component
public class EventBusRegister implements SmartInitializingSingleton {@Autowiredprivate ApplicationContext applicationContext;@Overridepublic void afterSingletonsInstantiated() {// 注册所有 @EventListener 的 BeanapplicationContext.getBeansWithAnnotation(MyEventListener.class).forEach((name, bean) -> EventBusFactory.getBus().register(bean));}
}

5.3.3. 🌟 场景三:注册中心上报(如 Dubbo、Nacos)

@Component
public class ServiceRegistry implements SmartInitializingSingleton {@Autowiredprivate ServiceMetaInfo localService;@Overridepublic void afterSingletonsInstantiated() {// 等所有 Bean 初始化完后统一注册服务信息registryCenter.register(localService);}
}

5.4. 源码调用顺序

Spring 在 DefaultListableBeanFactory.preInstantiateSingletons() 方法中:

if (bean instanceof SmartInitializingSingleton) {((SmartInitializingSingleton) bean).afterSingletonsInstantiated();
}

该方法会在所有单例 Bean 初始化完成后,逐个调用实现了 SmartInitializingSingleton 的类的回调方法

5.5. ✅ 总结对比

项目

InitializingBean

SmartInitializingSingleton

接口方法

afterPropertiesSet()

afterSingletonsInstantiated()

调用时机

Bean 实例化 + 属性注入后

所有单例实例化完成后

调用频率

每个 Bean 调一次

所有单例后只调一次(全局)

适用场景

Bean 自己的初始化

需要获取所有 Bean 后统一处理

替代注解

@PostConstruct

无明确替代,最末生命周期回调

5.6. ✅ 实战建议

需求

建议实现

初始化当前 Bean 用

@PostConstructInitializingBean

需要等待所有单例准备好后统一处理(如注册、扫描)

SmartInitializingSingleton

如果你在项目中需要:

  • 自动发现所有某类 Bean(如任务、监听器、规则处理器)
  • 注册第三方服务(Nacos、Dubbo、Job)
  • 需要确保 Spring 容器启动完成再初始化逻辑

推荐用 SmartInitializingSingleton,这是一种更稳妥的方式。

博文参考

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

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

相关文章

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…

冯诺依曼架构是什么?

冯诺依曼架构是什么&#xff1f; 冯诺依曼架构&#xff08;Von Neumann Architecture&#xff09;是现代计算机的基础设计框架&#xff0c;由数学家约翰冯诺依曼&#xff08;John von Neumann&#xff09;及其团队在1945年提出。其核心思想是通过统一存储程序与数据&#xff0…

【持续更新】linux网络编程试题

问题1 请简要说明TCP/IP协议栈的四层结构&#xff0c;并分别举出每一层出现的典型协议或应用。 答案 应用层&#xff1a;ping,telnet,dns 传输层&#xff1a;tcp,udp 网络层&#xff1a;ip,icmp 数据链路层&#xff1a;arp,rarp 问题2 下列协议或应用分别属于TCP/IP协议…

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…

React 基础入门笔记

一、JSX语法规则 1. 定义虚拟DOM时&#xff0c;不要写引号 2.标签中混入JS表达式时要用 {} &#xff08;1&#xff09;.JS表达式与JS语句&#xff08;代码&#xff09;的区别 &#xff08;2&#xff09;.使用案例 3.样式的类名指定不要用class&#xff0c;要用className 4.内…

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…

SQL进阶之旅 Day 19:统计信息与优化器提示

【SQL进阶之旅 Day 19】统计信息与优化器提示 文章简述 在数据库性能调优中&#xff0c;统计信息和优化器提示是两个至关重要的工具。统计信息帮助数据库优化器评估查询成本并选择最佳执行计划&#xff0c;而优化器提示则允许开发人员对优化器的行为进行微调。本文深入探讨了…

安宝特方案丨船舶智造AR+AI+作业标准化管理系统解决方案(维保)

船舶维保管理现状&#xff1a;设备维保主要由维修人员负责&#xff0c;根据设备运行状况和维护计划进行定期保养和故障维修。维修人员凭借经验判断设备故障原因&#xff0c;制定维修方案。 一、痛点与需求 1 Arbigtec 人工经验限制维修效率&#xff1a; 复杂设备故障的诊断和…

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…

基于区块链的供应链溯源系统:构建与实践

前言 在当今全球化的经济环境中&#xff0c;供应链的复杂性不断增加&#xff0c;商品从原材料采购到最终交付给消费者的过程涉及多个环节和众多参与者。如何确保供应链的透明度、可追溯性和安全性&#xff0c;成为企业和消费者关注的焦点。区块链技术以其去中心化、不可篡改和透…

Web攻防-SQL注入数据格式参数类型JSONXML编码加密符号闭合

知识点&#xff1a; 1、Web攻防-SQL注入-参数类型&参数格式 2、Web攻防-SQL注入-XML&JSON&BASE64等 3、Web攻防-SQL注入-数字字符搜索等符号绕过 案例说明&#xff1a; 在应用中&#xff0c;存在参数值为数字&#xff0c;字符时&#xff0c;符号的介入&#xff0c…

探秘鸿蒙 HarmonyOS NEXT:实战用 CodeGenie 构建鸿蒙应用页面

在开发鸿蒙应用时&#xff0c;你是否也曾为一个页面的布局反复调整&#xff1f;是否还在为查 API、写模板代码而浪费大量时间&#xff1f;今天带大家实战体验一下鸿蒙官方的 AI 编程助手——CodeGenie&#xff08;代码精灵&#xff09; &#xff0c;如何从 0 到 1 快速构建一个…

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…

使用Whisper本地部署实现香港版粤语+英语混合语音转文字方案

今天要一个非常好的朋友有个工作&#xff0c;就是要把医院医生诊断的说话记录转成文字&#xff0c;之前都是她本人一句一句的听&#xff0c;然后记录下来的&#xff0c;我想通过ai 来解决这个问题。 她的需求如下&#xff1a; 不能把数据传到网上&#xff0c;隐私问题所以需要…

案例分享--汽车制动卡钳DIC测量

制动系统是汽车的主要组成部分&#xff0c;是汽车的主要安全部件之一。随着车辆性能的不断提高&#xff0c;车速不断提升&#xff0c;对车辆的制动系统也随之提出了更高要求&#xff0c;因此了解车辆制动系统中每个部件的动态行为成为了制动系统优化的主要途径&#xff0c;同时…

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …