Spring Bean 生命周期自定义扩展实战示例
本文档聚焦于 Spring Bean 生命周期中最常用、最实用的自定义扩展场景,通过可直接运行的代码示例,展示不同扩展点的使用方式和典型业务场景。
前提说明
所有示例基于 Spring Boot 3.x 实现,核心依赖仅需引入 Spring Boot 基础依赖:
xml
<!-- pom.xml 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
扩展场景 1:初始化阶段加载配置 / 创建资源
场景描述
Bean 初始化时加载外部配置文件、创建数据库连接池、初始化缓存等(最常用的扩展场景)。
实现方式(推荐自定义方法,解耦)
import org.springframework.stereotype.Component;
import java.io.FileInputStream;
import java.util.Properties;
/**
* 示例:初始化时加载配置文件,销毁时关闭资源
*/
@Component
public class ResourceManagerBean {
// 模拟配置属性
private Properties configProps;
// 模拟资源(如连接池、文件流)
private FileInputStream configFileStream;
// 自定义初始化方法(通过 @Bean(initMethod) 或 @PostConstruct 注解)
// 推荐使用 @PostConstruct(无需在 @Bean 中配置,更简洁)
@javax.annotation.PostConstruct
public void initResource() {
System.out.println("【初始化扩展】加载配置文件并创建资源");
try {
// 1. 加载配置文件
configFileStream = new FileInputStream("application-custom.properties");
configProps = new Properties();
configProps.load(configFileStream);
System.out.println("加载配置:app.name = " + configProps.getProperty("app.name"));
// 2. 模拟创建数据库连接池/缓存等资源
// ... 业务逻辑 ...
} catch (Exception e) {
throw new RuntimeException("初始化资源失败", e);
}
}
// 自定义销毁方法(推荐 @PreDestroy)
@javax.annotation.PreDestroy
public void destroyResource() {
System.out.println("【销毁扩展】关闭资源释放连接");
try {
// 关闭文件流/连接池等资源
if (configFileStream != null) {
configFileStream.close();
}
// 清理缓存/关闭连接池
// ... 业务逻辑 ...
} catch (Exception e) {
throw new RuntimeException("销毁资源失败", e);
}
}
// 业务方法
public String getConfig(String key) {
return configProps.getProperty(key);
}
}
测试验证
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class BeanExtensionApplication {
public static void main(String[] args) {
// 启动容器(触发初始化)
ConfigurableApplicationContext context = SpringApplication.run(BeanExtensionApplication.class, args);
// 使用 Bean
ResourceManagerBean resourceBean = context.getBean(ResourceManagerBean.class);
System.out.println("业务使用:" + resourceBean.getConfig("app.name"));
// 关闭容器(触发销毁)
context.close();
}
}
输出结果
【初始化扩展】加载配置文件并创建资源
加载配置:app.name = SpringBeanExtensionDemo
业务使用:SpringBeanExtensionDemo
【销毁扩展】关闭资源释放连接
扩展场景 2:BeanPostProcessor 全局增强(统一处理所有 Bean)
场景描述
对容器中所有 / 指定 Bean 进行统一增强,如:
- 给所有 Bean 添加日志埋点
- 统一设置 Bean 属性
- 替换 Bean 实例(如动态代理)
实现示例(统一设置 Bean 属性 + 日志埋点)
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* 示例:BeanPostProcessor 全局增强
* 1. 给所有以 "demo" 开头的 Bean 添加统一标识
* 2. 记录所有 Bean 的初始化前后日志
*/
@Component
public class GlobalBeanEnhancer implements BeanPostProcessor {
// 初始化前置处理(所有 Bean 初始化前执行)
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("\n【BeanPostProcessor 前置】处理 Bean:" + beanName + ",类型:" + bean.getClass().getSimpleName());
// 对指定 Bean 增强:设置统一属性
if (beanName.startsWith("demo") && bean instanceof DemoBusinessBean) {
DemoBusinessBean demoBean = (DemoBusinessBean) bean;
demoBean.setSystemTag("GLOBAL_2026"); // 统一设置系统标识
System.out.println("为 " + beanName + " 设置系统标识:GLOBAL_2026");
}
return bean; // 返回原 Bean(可替换为代理对象)
}
// 初始化后置处理(所有 Bean 初始化后执行)
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("【BeanPostProcessor 后置】处理 Bean:" + beanName + ",初始化完成");
return bean;
}
}
// 测试用业务 Bean
@Component("demoOrderBean")
class DemoBusinessBean {
private String systemTag;
private String orderId;
@PostConstruct
public void init() {
System.out.println("DemoBusinessBean 初始化,系统标识:" + systemTag);
}
// getter/setter
public void setSystemTag(String systemTag) {
this.systemTag = systemTag;
}
}
输出结果
【BeanPostProcessor 前置】处理 Bean:demoOrderBean,类型:DemoBusinessBean
为 demoOrderBean 设置系统标识:GLOBAL_2026
DemoBusinessBean 初始化,系统标识:GLOBAL_2026
【BeanPostProcessor 后置】处理 Bean:demoOrderBean,初始化完成
扩展场景 3:Aware 接口感知容器信息
场景描述
Bean 需要获取容器内的核心信息(如 Bean 名称、ApplicationContext 实例),实现与 Spring 容器的交互。
实现示例(获取容器上下文 + Bean 名称)
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 示例:Aware 接口获取容器信息
* 1. 获取当前 Bean 的名称
* 2. 获取 ApplicationContext 实例,操作容器
*/
@Component
public class ContainerAwareBean implements BeanNameAware, ApplicationContextAware {
// 当前 Bean 名称
private String currentBeanName;
// Spring 容器上下文
private ApplicationContext applicationContext;
// 实现 BeanNameAware,获取 Bean 名称
@Override
public void setBeanName(String beanName) {
this.currentBeanName = beanName;
System.out.println("【Aware 扩展】获取当前 Bean 名称:" + beanName);
}
// 实现 ApplicationContextAware,获取容器上下文
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
System.out.println("【Aware 扩展】获取 ApplicationContext 实例");
// 示例:通过容器获取其他 Bean
DemoBusinessBean demoBean = applicationContext.getBean(DemoBusinessBean.class);
System.out.println("通过容器获取 DemoBusinessBean:" + demoBean);
}
// 业务方法:使用容器信息
public void doBusiness() {
System.out.println("当前 Bean 名称:" + currentBeanName);
// 获取容器中所有 Bean 名称
String[] allBeanNames = applicationContext.getBeanDefinitionNames();
System.out.println("容器中 Bean 总数:" + allBeanNames.length);
}
}
测试调用
// 在主类中添加
ContainerAwareBean awareBean = context.getBean(ContainerAwareBean.class);
awareBean.doBusiness();
输出结果
【Aware 扩展】获取当前 Bean 名称:containerAwareBean
【Aware 扩展】获取 ApplicationContext 实例
通过容器获取 DemoBusinessBean:com.example.DemoBusinessBean@xxxx
当前 Bean 名称:containerAwareBean
容器中 Bean 总数:xxx(根据实际加载的 Bean 数量)
扩展场景 4:原型 Bean 的自定义销毁(手动管理)
场景描述
原型(Prototype)Bean 的销毁方法不会被 Spring 容器自动调用,需手动触发销毁逻辑。
实现示例
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
/**
* 示例:原型 Bean 的自定义销毁
*/
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 原型作用域
public class PrototypeBean {
// 模拟资源
private String resourceId;
public PrototypeBean() {
this.resourceId = "proto_" + System.currentTimeMillis();
System.out.println("创建原型 Bean:" + resourceId);
}
// 自定义销毁方法(容器不会自动调用)
public void manualDestroy() {
System.out.println("手动销毁原型 Bean:" + resourceId);
// 清理资源逻辑
}
}
调用示例(手动销毁)
// 在主类中
// 获取原型 Bean(每次获取都是新实例)
PrototypeBean proto1 = context.getBean(PrototypeBean.class);
PrototypeBean proto2 = context.getBean(PrototypeBean.class);
// 业务使用后手动销毁
proto1.manualDestroy();
proto2.manualDestroy();
输出结果
创建原型 Bean:proto_1710000000000
创建原型 Bean:proto_1710000000001
手动销毁原型 Bean:proto_1710000000000
手动销毁原型 Bean:proto_1710000000001
总结
- 初始化 / 销毁扩展 :优先使用
@PostConstruct和@PreDestroy注解(解耦,无需实现接口),适合加载 / 释放资源、初始化配置等场景; - 全局 Bean 增强 :使用
BeanPostProcessor实现所有 / 指定 Bean 的统一修改、日志埋点、代理增强; - 容器信息感知 :通过
Aware接口获取 Bean 名称、ApplicationContext 等容器信息,实现与 Spring 容器的交互; - 原型 Bean 销毁:Spring 不自动管理,需手动调用自定义销毁方法清理资源。
这些扩展点覆盖了日常开发中 90% 以上的 Bean 生命周期自定义需求,优先选择注解式(@PostConstruct/@PreDestroy)而非接口式(InitializingBean/DisposableBean),可降低与 Spring 框架的耦合度。