前言
在日常使用 Spring 框架进行开发时,我们享受着依赖注入(IoC)带来的便利,但你是否曾好奇一个 Bean 是如何从字节码变成容器中一个 ready-to-use 的组件?理解 Spring Bean 的生命周期,它能让你真正驾驭 Spring,在复杂业务场景中实现更优雅的功能扩展。本文将带你从核心流程出发,逐步深入各个扩展点,并通过代码示例讲解其实际应用场景。
一、Spring Bean 生命周期的核心骨架
抛开所有扩展点,一个 Bean 最基础的生命周期非常简单,仅包含四个步骤:
- 实例化(Instantiation):通过构造函数或工厂方法创建 Bean 的实例。
- 属性填充(Population) :Spring 通过反射,完成依赖注入(为
@Autowired
、@Value
等注解标记的字段赋值)。 - 初始化(Initialization):如果指定了初始化方法,则调用它。
- 销毁(Destruction):在容器关闭时,如果指定了销毁方法,则调用它。
这是一个朴素的骨架,Spring 强大的扩展能力都基于此构建。
如下为 Spring Bean 最基础的生命周期示例代码:
- 定义一个bean:LifeCircleBean
java
public class LifeCircleBean {
private DependencyBean dependencyBean;
// 1. 实例化:构造函数
public LifeCircleBean() {
System.out.println("【1. 实例化】LifeCircleBean - 调用构造函数");
}
// 2. 属性填充:通过setter方法进行依赖注入
@Autowired
public void setDependencyBean(DependencyBean dependencyBean) {
System.out.println("【2. 属性填充】LifeCircleBean - 依赖注入 (setDependencyBean)");
this.dependencyBean = dependencyBean;
}
// 3. 初始化方法
public void init() {
System.out.println("【3. 初始化】LifeCircleBean - 自定义init-method被调用");
System.out.println("在init()中,dependencyBean 是否为空? " + (dependencyBean == null));
}
// 4. 销毁方法
public void destroy() {
System.out.println("【4. 销毁】LifeCircleBean - 自定义destroy-method被调用");
}
public void doSomething() {
System.out.println("LifeCircleBean is doing something");
}
}
class DependencyBean {
}
- 配置类
java
@Configuration
public class LifeCircleConfig {
@Bean
public DependencyBean dependencyBean() {
return new DependencyBean();
}
@Bean(initMethod = "init", destroyMethod = "destroy")
public LifeCircleBean lifeCircleBean() {
return new LifeCircleBean();
}
}
- 测试代码:
java
public class LifeCircleTest {
public static void main(String[] args) {
// 开始创建Spring应用上下文
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(LifeCircleConfig.class);
// LifeCircleBean 并使用
LifeCircleBean service = context.getBean(LifeCircleBean.class);
service.doSomething();
// 关闭容器
context.close();
}
}
输出结果:
console
【1. 实例化】LifeCircleBean - 调用构造函数
【2. 属性填充】LifeCircleBean - 依赖注入 (setDependencyBean)
【3. 初始化】LifeCircleBean - 自定义init-method被调用
在init()中,dependencyBean 是否为空? false
LifeCircleBean is doing something
【4. 销毁】LifeCircleBean - 自定义destroy-method被调用
二、Spring 官方接口扩展点
Spring 提供了两个接口,允许我们在 Bean 的生命周期中插入自定义逻辑。
1. InitializingBean
InitializingBean
接口提供了一个 afterPropertiesSet()
方法,它在属性填充完成后,在自定义初始化方法之前被调用。
应用场景:用于执行那些在 Bean 的依赖注入完成后,业务逻辑开始前,进行一些必须的复杂初始化工作。
- 初始化消息队列(如Kafka、RocketMQ)的生产者/消费者、缓存预热、支付网关客户端、OSS对象存储客户端等,并验证配置是否正确。
- 为何适用:保证外部依赖可用性,实现快速失败(Fail-Fast),避免运行时因配置错误或服务不可用导致故障。
基于接口 InitializingBean
实现配置验证和oss客户端初始化示例代码:
java
@Component
public class OssClient implements InitializingBean {
@Value("${oss.endpoint}")
private String endpoint;
@Value("${oss.accessKeyId}")
private String accessKeyId;
@Value("${oss.accessKeySecret}")
private String accessKeySecret;
private OSSClient ossClient;
// 实现 InitializingBean 接口,在属性填充完成后执行验证逻辑
@Override
public void afterPropertiesSet() throws Exception {
// 验证配置
if (StringUtils.isAnyBlank(endpoint, accessKeyId, accessKeySecret)) {
throw new IllegalStateException("OSS配置不完整");
}
// 初始化OSS客户端
ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
}
}
2. DisposableBean
DisposableBean
接口提供了一个 destroy()
方法,它在 Bean 被销毁时最先被调用。
应用场景:在 Bean 被容器销毁前,进行一些"清理工作",确保资源被正确释放,避免内存泄漏和数据不一致。
-
向自定义监控系统注册当前服务实例、启动后台任务线程(如定时刷新配置),并在应用关闭时安全地关闭这些线程、释放连接资源、反注册实例。
-
为何适用:确保资源有序释放,避免内存泄漏或数据不一致。例如,在容器销毁Bean之前(@PreDestroy 或 destroy())关闭资源。
基于DisposableBean
实现Nacos服务反注册示例代码:
java
// 这是一个模拟 NacosServiceRegistry 的简化类,展示原理
public class NacosServiceRegistry implements ServiceRegistry<Registration>, DisposableBean {
private final NamingService namingService; // Nacos 客户端
private Registration registration; // 当前服务的注册信息
private String instanceId; // 当前服务实例的ID
// 构造函数,接收依赖
public NacosServiceRegistry(NamingService namingService) {
this.namingService = namingService;
}
// 实现 DisposableBean 接口,在Bean销毁时自动回调
@Override
public void destroy() throws Exception {
// 执行反注册逻辑
deregister();
}
// 反注册方法
@Override
public void deregister(Registration registration) {
try {
// 调用 Nacos 客户端进行反注册
namingService.deregisterInstance(registration.getServiceId(), registration.getHost(), registration.getPort());
System.out.println("Service deregistered from Nacos: " + registration.getServiceId());
} catch (NacosException e) {
throw new RuntimeException("Failed to deregister service", e);
}
}
// ... 其他方法
}
注意 :现在的NacosServiceRegistry
类实现注册和反注册并非通过实现以上2个接口实现的,而是通过以下两种方式协同工作:
ApplicationListener
:监听 Spring 容器的关闭事件- 注册时机:监听 WebServerInitializedEvent 事件
- 反注册时机:由独立的Bean监听 ContextClosedEvent 事件
AbstractAutoServiceRegistration
:一个抽象父类,它提供了基于事件的注册/反注册模板方法。
三、Java 标准注解扩展点 (JSR-250)
为了解耦,我们通常使用 Java 公共注解(JSR-250)来定义初始化和销毁方法。
1. @PostConstruct
@PostConstruct
注解标记的方法,在 Bean 完成依赖注入后立即执行,效果等同于 InitializingBean.afterPropertiesSet()
,但无侵入性。
资源初始化和缓存预热
示例代码:
java
@Service
public class CacheService {
@Autowired
private ProductRepository productRepository;
private Map<Long, Product> cache;
@PostConstruct
public void preloadCache() {
// 将商品信息加载到本地缓存
List<Product> products = productRepository.findAll();
cache = products.stream()
.collect(Collectors.toMap(Product::getId, Function.identity()));
}
}
2. @PreDestroy
@PreDestroy
注解标记的方法,在 Bean 被容器销毁之前执行,效果等同于 DisposableBean.destroy()
,但无侵入性。
安全关闭线程池示例代码:
java
@Service
public class BackgroundTaskService {
private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
@PostConstruct
public void startTask() {
scheduler.scheduleAtFixedRate(this::refresh, 1, 1, TimeUnit.HOURS);
}
@PreDestroy
public void shutdown() {
// 优雅关闭,等待已有任务完成
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(10, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
}
}
}
加入扩展的初始化和销毁后的生命周期
如下为 Spring Bean 加入扩展点的生命周期示例代码:
- 定义一个bean:ExtLifeCircleBean
java
public class ExtLifeCircleBean implements InitializingBean, DisposableBean {
private DependencyBean dependencyBean;
// 1. 实例化:构造函数
public ExtLifeCircleBean() {
System.out.println("【1. 实例化】ExtLifeCircleBean - 调用构造函数");
}
// 2. 属性填充:通过setter方法进行依赖注入
@Autowired
public void setDependencyBean(DependencyBean dependencyBean) {
System.out.println("【2. 属性填充】ExtLifeCircleBean - 依赖注入 (setDependencyBean)");
this.dependencyBean = dependencyBean;
}
// 3. 初始化方法
// 3.1 @PostConstruct
@PostConstruct
public void postConstruct() {
System.out.println("【3.1 初始化】ExtLifeCircleBean - @PostConstruct注解方法被调用");
}
// 3.2 InitializingBean.afterPropertiesSet
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("【3.2 初始化】ExtLifeCircleBean - 实现InitializingBean.afterPropertiesSet方法被调用");
}
// 3.3 Bean指定init-method
public void init() {
System.out.println("【3.3 初始化】ExtLifeCircleBean - 自定义init-method被调用");
System.out.println("在init()中,dependencyBean 是否为空? " + (dependencyBean == null));
}
// 4. 销毁方法
// 4.1 @PreDestroy
@PreDestroy
public void preDestroy() {
System.out.println("【4.1 销毁】ExtLifeCircleBean - @PreDestroy注解方法被调用");
}
// 4.2 DisposableBean.destroy
@Override
public void destroy() throws Exception {
System.out.println("【4.2 销毁】ExtLifeCircleBean - 实现DisposableBean.destroy方法被调用");
}
// 4.3 @Bean 指定 destroy-method
public void annoDestroy() {
System.out.println("【4.3 销毁】ExtLifeCircleBean - 自定义destroy-method被调用");
}
public void doSomething() {
System.out.println("ExtLifeCircleBean is doing something");
}
}
- 配置类
java
@Configuration
public class LifeCircleConfig {
@Bean
public DependencyBean dependencyBean() {
return new DependencyBean();
}
@Bean(initMethod = "init", destroyMethod = "annoDestroy")
public ExtLifeCircleBean extLifeCircleBean() {
return new ExtLifeCircleBean();
}
}
- 测试代码:
java
public class LifeCircleTest {
public static void main(String[] args) {
// 开始创建Spring应用上下文
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(LifeCircleConfig.class);
ExtLifeCircleBean service = context.getBean(ExtLifeCircleBean.class);
service.doSomething();
// 关闭容器
context.close();
}
}
输出结果:
console
【1. 实例化】ExtLifeCircleBean - 调用构造函数
【2. 属性填充】ExtLifeCircleBean - 依赖注入 (setDependencyBean)
【3.1 初始化】ExtLifeCircleBean - @PostConstruct注解方法被调用
【3.2 初始化】ExtLifeCircleBean - 实现InitializingBean.afterPropertiesSet方法被调用
【3.3 初始化】ExtLifeCircleBean - 自定义init-method被调用
在init()中,dependencyBean 是否为空? false
ExtLifeCircleBean is doing something
【4.1 销毁】ExtLifeCircleBean - @PreDestroy注解方法被调用
【4.2 销毁】ExtLifeCircleBean - 实现DisposableBean.destroy方法被调用
【4.3 销毁】ExtLifeCircleBean - 自定义destroy-method被调用
加入标准注解后的生命周期顺序如下所示:
四、最强大的扩展点:BeanPostProcessor
BeanPostProcessor
是 Spring 框架中最强大、最核心的扩展点之一 。它是一个"后处理器",它有两个方法,允许你在 Bean 实例化、依赖注入完成后,在初始化回调(如 InitializingBean
, @PostConstruct
) 执行之前 和之后,对 Bean 本身进行干预。
工作原理 :Spring 容器在创建每个 Bean 时,都会遍历应用上下文中所有实现了 BeanPostProcessor
接口的 Bean,并依次调用它们的方法。这是一个典型的责任链模式。
BeanPostProcessor
中定义的2个方法:
java
public interface BeanPostProcessor {
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
1. Spring 框架内 BeanPostProcessor 的广泛应用
Spring 框架内部大量使用了 BeanPostProcessor
来实现关键功能:
-
AutowiredAnnotationBeanPostProcessor
:- 功能 :负责处理
@Autowired
和@Value
注解。 - 时机 :主要在
postProcessProperties()
(这是InstantiationAwareBeanPostProcessor
接口的方法,是BeanPostProcessor
的子接口) 中完成依赖注入。
- 功能 :负责处理
-
CommonAnnotationBeanPostProcessor
:- 功能 :处理 Java 标准注解,如
@PostConstruct
,@PreDestroy
,@Resource
。 - 时机 :
@PostConstruct
方法的发现和注册是在后处理阶段完成的。
- 功能 :处理 Java 标准注解,如
-
ApplicationContextAwareProcessor
:- 功能 :负责回调各种
*Aware
接口(如ApplicationContextAware
,BeanNameAware
,EnvironmentAware
)。 - 时机 :在
postProcessBeforeInitialization
中检查并调用这些 Aware 接口的方法,为 Bean 注入容器相关信息。
- 功能 :负责回调各种
2. 自定义 BeanPostProcessor 实现日志打印
代码示例:实现一个方法调用日志处理器
java
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ExtLifeCircleBean) {
System.out.println("【3 之前】LoggingBeanPostProcessor.postProcessBeforeInitialization... before...");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ExtLifeCircleBean) { // 只为特定类创建代理对象
System.out.println("【3 之后】LoggingBeanPostProcessor.postProcessAfterInitialization... after...");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(bean.getClass());
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println(">>>>>>>>>>>>>> CGLIB Proxy - Before: " + method.getName());
// 调用原始方法
Object result = proxy.invokeSuper(obj, args);
System.out.println(">>>>>>>>>>>>>> CGLIB Proxy - After: " + method.getName());
return result;
});
return enhancer.create();
}
// 如果不是特定的类,直接返回原始Bean
return bean;
}
}
修改配置类代码:增加@ComponentScan(basePackages = "xxx.xxx.xxx")
java
@Configuration
@ComponentScan(basePackages = "xxx.xxx.xxx")
public class LifeCircleConfig {
// 其他不变
}
输出结果:
console
【1. 实例化】ExtLifeCircleBean - 调用构造函数
【2. 属性填充】ExtLifeCircleBean - 依赖注入 (setDependencyBean)
【3 之前】LoggingBeanPostProcessor.postProcessBeforeInitialization... before...
【3.1 初始化】ExtLifeCircleBean - @PostConstruct注解方法被调用
【3.2 初始化】ExtLifeCircleBean - 实现InitializingBean.afterPropertiesSet方法被调用
【3.3 初始化】ExtLifeCircleBean - 自定义init-method被调用
在init()中,dependencyBean 是否为空? false
【3 之后】LoggingBeanPostProcessor.postProcessAfterInitialization... after...
【1. 实例化】ExtLifeCircleBean - 调用构造函数
>>>>>>>>>>>>>> CGLIB Proxy - Before: doSomething
ExtLifeCircleBean is doing something
>>>>>>>>>>>>>> CGLIB Proxy - After: doSomething
【4.1 销毁】ExtLifeCircleBean - @PreDestroy注解方法被调用
【4.2 销毁】ExtLifeCircleBean - 实现DisposableBean.destroy方法被调用
【4.3 销毁】ExtLifeCircleBean - 自定义destroy-method被调用
从结果可以看到在调用doSomething
方法前后都打印了日志,说明定义的日志处理器生效了,并且从Spring容器中拿到的是一个代理对象而不再是一个原始对象了,其类型为ExtLifeCircleBean$$EnhancerByCGLIB$$c8380ae5
。
从结果中还可以看到打印了2次"【1. 实例化】ExtLifeCircleBean - 调用构造函数",这是因为 CGLIB
通过继承机制创建代理,而 Java 中的继承机制要求子类构造方法必须调用父类构造方法。
3. 执行顺序的重要性
多个 BeanPostProcessor
的执行顺序非常关键。Spring 允许通过实现 Ordered
接口或使用 @Order
注解来定义顺序。例如,AutowiredAnnotationBeanPostProcessor
必须在你的自定义后处理器之前执行,否则依赖注入可能无法正常工作。
4. 最佳实践
- 轻量操作 :在
BeanPostProcessor
中应保持逻辑轻量,因为它会影响每一个 Bean 的创建过程。 - 避免代理链 :谨慎地在
BeanPostProcessor
中创建代理,多个后处理器可能会创建多层代理,影响性能和调试。
总结
以上是关于Spring Bean生命周期的全部内容,首先是4个基本的生命周期阶段,然后分别讲了Spring官方接口扩展点和JDK标准注解扩展点,最后讲了BeanPostProcessor
实现全局扩展。
通过理解 Spring Bean 的生命周期,是从"会用 Spring"到"懂 Spring"的关键一步。它不再是黑盒,而是一个提供了丰富扩展点的标准化流程。我们可以通过实现接口、使用注解,特别是利用强大的 BeanPostProcessor
,在生命周期的各个阶段注入自定义逻辑,从而实现诸如事务管理、AOP、监控、安全等高级功能。
最后,让我们用一张完整的流程图来回顾并总结本文的内容:
前置处理] C --> D[初始化] subgraph D [初始化序列] D1["@PostConstruct 方法"] D2["InitializingBean.afterPropertiesSet"] D3["自定义 init-method"] end D --> E["BeanPostProcessor 后置处理
(AOP在此创建代理)"] E --> F[Bean 准备就绪] F --> G[容器关闭] G --> H[销毁] subgraph H [销毁序列] H1["@PreDestroy 方法"] H2[DisposableBean.destroy] H3["自定义 destroy-method"] end