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 初始化(注入属性)后执行自定义逻辑 |
替代 | 是一种“接口驱动”的初始化方式 |
适用于框架开发 | 明确初始化点,便于统一管理 |
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.annotation
或 jakarta.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
的对比
特性 |
|
|
来源 | Java 标准注解 | Spring 接口 |
侵入性 | 低(无需实现接口) | 高(必须实现接口) |
可读性 | 更清晰、简洁 | 稍显繁琐 |
推荐 | ✅ 推荐 | ❌ 一般不推荐用于业务代码 |
场景 | 普通业务初始化 | 框架、组件级初始化 |
3. @PostConstruct
和 InitializingBean
区别?
在 缓存初始化 场景中,@PostConstruct
和 InitializingBean
都能完成初始化逻辑,但两者有以下核心区别:
@PostConstruct
是注解驱动的初始化方式,简洁、解耦、推荐用于业务代码;InitializingBean
是接口驱动的初始化方式,侵入性强,推荐用于框架/中间件级别代码。
3.1. ✅ 功能与使用上的对比
对比项 |
|
|
本质 | Java 标准注解(JSR-250) | Spring 生命周期接口 |
编码方式 | 在方法上加注解 | 实现接口、重写方法 |
方法名 | 任意(如 | 固定: |
方法个数 | 可以多个类中各定义一个 | 一个类只能有一个 |
侵入性 | ✅ 低:无须继承或实现接口 | ❌ 高:必须实现接口 |
可读性 | ✅ 强:一看注解就知道是初始化 | ❌ 差:容易被忽视 |
可测试性 | ✅ 好(不会影响类结构) | ❌ 接口实现影响结构 |
场景适用 | 业务代码、工具类、缓存加载 | 框架设计、底层组件、可复用模块 |
推荐程度 | ✅ 更推荐使用 | ⚠️ 更适合框架代码 |
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. ✅ 实际开发推荐
场景 | 推荐方式 |
普通业务初始化(如缓存、参数) | ✅ |
框架开发 / 高通用组件(如连接池、调度器) | ✅ |
初始化逻辑中涉及多个阶段、多个顺序 | ❌都不推荐,建议用 |
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. 🔍 区别总结:
对比项 |
|
|
来源 | Spring 接口 | Java 标准注解(JSR-250) |
入侵性 | 高(实现接口) | 低(注解) |
推荐程度 | ❌ 较低 | ✅ 更推荐 |
方法限制 | 固定 | 方法名可自定义 |
结构清晰 | ❌ 不利于多继承 | ✅ 解耦、灵活 |
4.6. ✅ 总结
项目 | 说明 |
接口名 |
|
方法 |
|
调用时机 | Bean 被销毁前(如容器关闭) |
常见用途 | 释放线程池、关闭连接池、清理缓存等 |
推荐替代 | 使用 |
使用场景 | 资源管理类、任务调度类、连接池等生命周期敏感组件 |
如果你在业务中需要在 Spring 应用关闭时清理资源,推荐使用 @PreDestroy
;如果你正在写一个框架、组件,或者需要精确控制 Bean 生命周期,则可以使用 DisposableBean
。