Bean 的生命周期
Bean 的生命周期是指一个 Bean 从被 Spring 容器创建到最终被销毁的整个过程。理解这个过程,你就能知道在 Bean 的不同阶段可以做哪些事情(比如初始化资源、释放资源)。
Bean 的生命周期过程相对复杂,特别是初始化阶段,有很多可以插入自定义逻辑的地方。我们以一个单例 Bean 为例,它的生命周期大致如下:
-
Bean 定义加载 (Loading the Bean Definition):
- Spring 容器启动时,会读取你的配置信息(XML、注解、Java Config)。
- 容器解析这些配置,生成 Bean 的定义信息(BeanDefinition),包括 Bean 的类名、作用域、依赖关系、属性值等。
-
Bean 实例化 (Bean Instantiation):
- 根据 Bean 定义,Spring 使用反射机制创建 Bean 的实例。这通常是通过调用 Bean 的构造器来完成的。
- 此时,Bean 只是一个“裸”对象,它的属性还没有被填充,依赖也还没有被注入。
-
属性填充 (Property Population) / 依赖注入 (Dependency Injection):
- Spring 根据 Bean 定义中的依赖关系和属性配置,将 Bean 依赖的其他 Bean 实例注入到当前 Bean 的对应属性中(通过 Setter 方法或直接设置字段)。
- 如果使用了构造器注入,这一步在实例化时就完成了。
-
执行 BeanPostProcessor 的
postProcessBeforeInitialization
方法:- 在 Bean 完成属性填充后,但在任何初始化方法被调用之前,Spring 会调用所有实现了
BeanPostProcessor
接口的 Bean 的postProcessBeforeInitialization
方法。 - 你可以在这个阶段对 Bean 进行一些预处理或修改。
- 在 Bean 完成属性填充后,但在任何初始化方法被调用之前,Spring 会调用所有实现了
-
执行初始化回调 (Initialization Callbacks):
- 这是 Bean 准备就绪的关键阶段。Spring 会按照特定的顺序调用 Bean 的初始化方法:
- 如果 Bean 实现了
org.springframework.beans.factory.InitializingBean
接口,会调用其afterPropertiesSet()
方法。 - 如果 Bean 定义中指定了
init-method
(XML 配置)或使用了@Bean
的initMethod
属性(Java Config),会调用指定的方法。 - 如果 Bean 类中有使用
@PostConstruct
注解的方法,会调用这些方法。
- 如果 Bean 实现了
- 这些方法通常用于执行 Bean 启动时必须完成的任务,比如初始化连接池、加载配置文件、执行一些自检等。
- 这是 Bean 准备就绪的关键阶段。Spring 会按照特定的顺序调用 Bean 的初始化方法:
-
执行 BeanPostProcessor 的
postProcessAfterInitialization
方法:- 在 Bean 完成所有初始化方法调用后,Spring 会调用所有实现了
BeanPostProcessor
接口的 Bean 的postProcessAfterInitialization
方法。 - 这个阶段非常重要,Spring 的 AOP 功能通常就是在这个阶段通过创建 Bean 的代理对象来实现的。当你从容器获取 Bean 时,如果它被 AOP 代理了,你拿到的实际上是这个代理对象。(如果这里没明白最后有补充说明😀)
- 在 Bean 完成所有初始化方法调用后,Spring 会调用所有实现了
-
Bean 准备就绪,可以使用 (Bean is Ready for Use):
- 经过以上步骤,Bean 已经完全配置和初始化完成,可以被应用程序的其他部分使用了。
- 当你通过
applicationContext.getBean()
获取 Bean 或通过@Autowired
注入 Bean 时,拿到的就是这个已经准备好的实例(对于单例)。
-
容器关闭,执行销毁回调 (Container Shutdown, Destruction Callbacks):
- 当 Spring 容器关闭时(比如 Web 应用停止,或者调用
applicationContext.close()
),Spring 会对那些需要销毁的 Bean(主要是单例 Bean)执行销毁操作。 - 销毁方法调用的顺序:
- 如果 Bean 类中有使用
@PreDestroy
注解的方法,会调用这些方法。 - 如果 Bean 实现了
org.springframework.beans.factory.DisposableBean
接口,会调用其destroy()
方法。 - 如果 Bean 定义中指定了
destroy-method
(XML 配置)或使用了@Bean
的destroyMethod
属性(Java Config),会调用指定的方法。
- 如果 Bean 类中有使用
- 这些方法通常用于释放 Bean 占用的资源,比如关闭数据库连接、关闭文件流等。
- 当 Spring 容器关闭时(比如 Web 应用停止,或者调用
原型 (Prototype) Bean 的生命周期区别:
对于原型 Bean,Spring 容器只负责到步骤 6(执行 postProcessAfterInitialization
)为止。一旦 Bean 被创建并交给请求者,Spring 就不再管理它了。步骤 8 的销毁回调不会由 Spring 容器自动执行。你需要自己负责管理原型 Bean 的销毁。
举个例子:
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
// @Component 注解告诉 Spring 这是一个 Bean,默认是 singleton 作用域
@Component
public class MyLifecycleBean implements InitializingBean, DisposableBean {
private String message; // 这是一个属性,用于演示属性填充
// --- Bean 生命周期步骤开始 ---
// 步骤 2: 实例化 (Instantiation)
// Spring 通过调用 Bean 的构造器来创建 Bean 的实例对象
public MyLifecycleBean() {
System.out.println("--- Step 2: MyLifecycleBean: Constructor called (实例化) ---");
// 此时对象刚创建,属性 message 还是 null
}
// 步骤 3: 属性填充 / 依赖注入 (Property Population / Dependency Injection)
// 如果 Bean 定义中有属性需要设置(比如通过 <property> 或 @Value),或者有依赖需要注入(@Autowired),
// Spring 会在实例化后调用相应的 Setter 方法或直接设置字段。
public void setMessage(String message) {
this.message = message;
System.out.println("--- Step 3: MyLifecycleBean: Setter for message called (属性填充/依赖注入) ---");
// 此时 message 属性已经被 Spring 设置了值
}
// --- 步骤 4: BeanPostProcessor.postProcessBeforeInitialization 在这里执行 ---
// Spring 会调用所有 BeanPostProcessor 的 postProcessBeforeInitialization 方法
// 可以在 Bean 的初始化方法调用前进行一些处理
// 步骤 5: 执行初始化回调 (Initialization Callbacks)
// Spring 会按特定顺序调用 Bean 的初始化方法。这些方法标志着 Bean 已经准备好被使用了。
// 5a: @PostConstruct 注解方法
// 这是 JSR-250 规范定义的初始化回调,Spring 支持它。
@PostConstruct
public void postConstructMethod() {
System.out.println("--- Step 5a: MyLifecycleBean: @PostConstruct method called ---");
// 适合在这里执行一些依赖注入完成后,Bean 首次可以使用前的初始化逻辑
}
// 5b: InitializingBean 接口的 afterPropertiesSet() 方法
// 这是 Spring 定义的初始化接口。
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("--- Step 5b: MyLifecycleBean: InitializingBean.afterPropertiesSet() called ---");
// 适合在这里进行一些依赖注入完成后的验证或初始化操作
if (this.message == null) {
this.message = "Default Message from afterPropertiesSet"; // 可以在这里设置默认值
}
}
// 5c: 自定义 init-method (通过 @Bean(initMethod="customInitMethod") 或 XML 配置指定)
// 这是通过配置指定的初始化方法。
public void customInitMethod() {
System.out.println("--- Step 5c: MyLifecycleBean: Custom init-method called ---");
// 适合在这里执行自定义的初始化逻辑
}
// --- 步骤 6: BeanPostProcessor.postProcessAfterInitialization 在这里执行 ---
// Spring 会调用所有 BeanPostProcessor 的 postProcessAfterInitialization 方法。
// AOP 代理的创建通常发生在这个阶段。如果 Bean 被代理,这里返回的是代理对象。
// 步骤 7: Bean 准备就绪,可以使用 (Bean is Ready for Use)
// Bean 已经完全初始化并配置完成,可以被其他 Bean 注入或直接获取使用了。
// doSomething() 是一个普通的业务方法,不是生命周期回调,它在 Bean 准备就绪后才能被调用。
public void doSomething() {
System.out.println("--- Step 7: MyLifecycleBean: doSomething() called. Message: " + message + " ---");
}
// --- Bean 生命周期结束,容器关闭 ---
// 步骤 8: 容器关闭,执行销毁回调 (Destruction Callbacks)
// 当 Spring 容器关闭时,会调用 Bean 的销毁方法来释放资源。
// 8a: @PreDestroy 注解方法
// 这是 JSR-250 规范定义的销毁回调,Spring 支持它。
@PreDestroy
public void preDestroyMethod() {
System.out.println("--- Step 8a: MyLifecycleBean: @PreDestroy method called ---");
// 适合在这里释放资源,比如关闭文件流、网络连接等
}
// 8b: DisposableBean 接口的 destroy() 方法
// 这是 Spring 定义的销毁接口。
@Override
public void destroy() throws Exception {
System.out.println("--- Step 8b: MyLifecycleBean: DisposableBean.destroy() called ---");
// 适合在这里释放资源
}
// 8c: 自定义 destroy-method (通过 @Bean(destroyMethod="customDestroyMethod") 或 XML 配置指定)
// 这是通过配置指定的销毁方法。
public void customDestroyMethod() {
System.out.println("--- Step 8c: MyLifecycleBean: Custom destroy-method called ---");
// 适合在这里执行自定义的清理逻辑
}
// --- Bean 生命周期结束 ---
}
如何运行并观察输出?
你需要一个 Spring 容器来运行这个例子。最简单的方式是使用 AnnotationConfigApplicationContext
:
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 配置类,用于定义 Bean
@Configuration
public class AppConfig {
@Bean(initMethod = "customInitMethod", destroyMethod = "customDestroyMethod")
public MyLifecycleBean myLifecycleBean() {
MyLifecycleBean bean = new MyLifecycleBean();
bean.setMessage("Hello from Config"); // 通过 Setter 设置属性
return bean;
}
}
// 主程序
public class MainApplication {
public static void main(String[] args) {
System.out.println("--- Spring Container Starting ---");
// 创建并启动 Spring 容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println("--- Spring Container Started ---");
// 从容器获取 Bean 并使用
MyLifecycleBean myBean = context.getBean(MyLifecycleBean.class);
myBean.doSomething();
System.out.println("--- Using Bean finished ---");
// 关闭容器,触发销毁回调
context.close();
System.out.println("--- Spring Container Closed ---");
}
}
运行 MainApplication
的 main
方法,你会在控制台看到类似以下的输出顺序:
--- Spring Container Starting ---
--- Step 2: MyLifecycleBean: Constructor called (实例化) ---
--- Step 3: MyLifecycleBean: Setter for message called (属性填充/依赖注入) ---
--- Step 5a: MyLifecycleBean: @PostConstruct method called ---
--- Step 5b: MyLifecycleBean: InitializingBean.afterPropertiesSet() called ---
--- Step 5c: MyLifecycleBean: Custom init-method called ---
--- Spring Container Started ---
--- Step 7: MyLifecycleBean: doSomething() called. Message: Hello from Config ---
--- Using Bean finished ---
--- Step 8a: MyLifecycleBean: @PreDestroy method called ---
--- Step 8b: MyLifecycleBean: DisposableBean.destroy() called ---
--- Step 8c: MyLifecycleBean: Custom destroy-method called ---
--- Spring Container Closed ---
有两处地方很明显可以注意到:步骤中的子步骤(比如初始化阶段的 5a, 5b, 5c 和销毁阶段的 8a, 8b, 8c),他们不是说一个 Bean 必须要有多种方法或接口需要关闭/初始化。
它们的含义是:
- Spring 提供了多种方式让你定义 Bean 的初始化和销毁逻辑。
- 你可以通过实现特定的接口 (
InitializingBean
,DisposableBean
)。 - 你可以通过使用特定的注解 (
@PostConstruct
,@PreDestroy
)。 - 你也可以通过配置(XML 或
@Bean
的initMethod
/destroyMethod
属性)指定一个普通方法作为初始化或销毁方法。
- 你可以通过实现特定的接口 (
- 一个 Bean 可以选择使用其中的一种、多种甚至不使用任何一种方式来定义初始化或销毁回调。
- 一个简单的 Bean 可能没有任何初始化或销毁逻辑。
- 一个 Bean 可能只使用了
@PostConstruct
来初始化。 - 一个 Bean 也可能同时实现了
InitializingBean
接口并且使用了@PreDestroy
注解。
- 如果一个 Bean 定义了多种初始化回调(或多种销毁回调),Spring 会按照一个固定的顺序依次调用它们。
步骤6详解:
首先,我们先简单回顾一下 AOP 的核心思想。
AOP (面向切面编程) 是什么?
AOP 是一种编程范式,它的主要目的是将那些横跨多个模块的功能(称为横切关注点,Cross-cutting Concerns)从核心业务逻辑中分离出来。
常见的横切关注点有:
- 事务管理 (Transactions): 在方法执行前开启事务,执行后提交或回滚。
- 日志记录 (Logging): 在方法执行前或后记录日志。
- 安全检查 (Security): 在方法执行前进行权限验证。
- 缓存 (Caching): 在方法执行前检查缓存,执行后更新缓存。
如果没有 AOP,这些功能代码会散布在你的业务逻辑代码中,导致代码重复、难以维护。
AOP 通过切面 (Aspect) 来实现这些横切关注点。一个切面通常包含:
- 通知 (Advice): 实际要执行的代码(比如开启事务、记录日志)。
- 切入点 (Pointcut): 定义通知要在哪些地方(哪些方法)执行。
Spring AOP 如何实现 AOP?
Spring AOP 默认使用的是动态代理 (Dynamic Proxy)。
- 代理 (Proxy): 代理是一个对象,它“包装”了另一个对象(称为目标对象,Target Object)。当你调用代理对象的方法时,代理可以在调用目标对象的方法之前或之后插入额外的逻辑。
- 动态: 代理对象不是你手动编写的,而是在程序运行时由 Spring 动态生成的。
Spring 会根据你的 AOP 配置(比如 @Aspect
注解、XML 配置),判断哪些 Bean 的哪些方法需要被“增强”(即应用切面)。对于这些 Bean,Spring 不会直接把原始的 Bean 实例注入到其他地方,而是会创建一个代理对象,然后把这个代理对象注入到其他需要依赖它的地方。
现在,我们把 AOP 代理创建和 Bean 的生命周期结合起来:
还记得 Bean 的生命周期步骤吗?
- 加载 Bean 定义
- 实例化 Bean (调用构造器)
- 属性填充/依赖注入 (调用 Setter 或设置字段)
- 执行
BeanPostProcessor.postProcessBeforeInitialization
- 执行初始化回调 (
@PostConstruct
,InitializingBean
,init-method
) - 执行
BeanPostProcessor.postProcessAfterInitialization
- Bean 准备就绪,可以使用
关键就在第 6 步:执行 BeanPostProcessor.postProcessAfterInitialization
。
Spring 内部有很多内置的 BeanPostProcessor
实现类,其中就包括负责处理 AOP 的 BeanPostProcessor
(比如 AnnotationAwareAspectJAutoProxyCreator
,它处理基于注解的 AOP)。
当 Spring 容器创建并初始化好一个 Bean(完成了步骤 1-5)后,就会把这个原始的 Bean 实例传递给所有注册的 BeanPostProcessor
的 postProcessAfterInitialization
方法。
Spring 的 AOP BeanPostProcessor
在接收到这个原始 Bean 实例后,会做以下判断:
- 检查这个 Bean 的类或者它的方法上是否有需要应用 AOP 的注解(比如
@Transactional
,@PreAuthorize
)? - 或者,检查是否有 AOP 配置(比如 XML 中的
<aop:config>
)定义了切入点,并且这个 Bean 的方法匹配了这些切入点?
如果判断结果是“是”,这个 Bean 需要被 AOP 增强:
- 这个 AOP
BeanPostProcessor
会利用 Java 的动态代理机制(或者 CGLIB 库,如果目标对象没有实现接口)为这个原始 Bean 实例创建一个代理对象。 - 这个代理对象“知道”如何执行原始 Bean 的方法,并且在执行前后会插入 AOP 切面中定义的逻辑(比如事务的开启/提交/回滚)。
- 最重要的一点:
postProcessAfterInitialization
方法的返回值就是这个新创建的代理对象,而不是原始的 Bean 实例。
如果判断结果是“否”,这个 Bean 不需要被 AOP 增强:
- 这个 AOP
BeanPostProcessor
什么也不做。 postProcessAfterInitialization
方法直接返回原始的 Bean 实例。
所以,当你从 Spring 容器获取一个 Bean(通过 @Autowired
或 getBean()
)时:
- 如果这个 Bean 没有被 AOP 代理,你拿到的是它原始的实例。
- 如果这个 Bean 被 AOP 代理了,你拿到的是 Spring 在
postProcessAfterInitialization
阶段为你创建的那个代理对象。
为什么这样做?
因为其他 Bean 依赖的是这个被增强后的功能(比如需要事务支持)。通过注入代理对象,当其他 Bean 调用被增强的方法时,实际上是调用了代理对象的方法。代理对象会先执行事务逻辑,然后再调用原始 Bean 的方法,从而实现了事务的自动管理,而调用者完全不需要关心事务的细节。
举个例子:
假设你有一个 OrderService
Bean,它的 placeOrder()
方法需要事务支持,所以你加了 @Transactional
注解。
@Service
public class OrderService {
@Autowired
private OrderDao orderDao; // 假设 OrderDao 是另一个 Bean
@Transactional // 这个方法需要事务
public void placeOrder(Order order) {
System.out.println("--- OrderService: placeOrder method called ---");
// 1. 保存订单到数据库
orderDao.save(order);
// 2. 更新库存 (假设这里可能出错)
// inventoryService.updateStock(...);
// ... 其他业务逻辑 ...
System.out.println("--- OrderService: placeOrder method finished ---");
}
}
@Component
public class AnotherComponent {
@Autowired
private OrderService orderService; // 注入 OrderService
public void processOrder() {
System.out.println("--- AnotherComponent: Calling placeOrder ---");
orderService.placeOrder(new Order()); // 调用 OrderService 的方法
System.out.println("--- AnotherComponent: placeOrder call finished ---");
}
}
Bean 的生命周期过程:
- Spring 容器启动,加载
OrderService
和AnotherComponent
的 Bean 定义。 - 实例化
OrderService
和AnotherComponent
。 - 为
OrderService
注入orderDao
。 - 执行
BeanPostProcessor.postProcessBeforeInitialization
(对OrderService
和AnotherComponent
)。 - 执行
OrderService
和AnotherComponent
的初始化回调 (@PostConstruct
等)。 - 执行
BeanPostProcessor.postProcessAfterInitialization
:- 当轮到
OrderService
时,Spring 的 AOPBeanPostProcessor
发现placeOrder
方法上有@Transactional
注解。 - 它会为原始的
OrderService
实例创建一个代理对象。 postProcessAfterInitialization
方法返回这个代理对象。- 当轮到
AnotherComponent
时,它没有 AOP 配置,postProcessAfterInitialization
直接返回原始的AnotherComponent
实例。
- 当轮到
- Bean 准备就绪:
OrderService
在容器中注册的是那个代理对象。AnotherComponent
在容器中注册的是它的原始实例。
- 依赖注入: 当 Spring 为
AnotherComponent
注入orderService
时,它从容器中获取OrderService
Bean,拿到的是那个代理对象,并将这个代理对象注入到AnotherComponent
的orderService
字段中。 - 使用 Bean: 当
AnotherComponent
调用orderService.placeOrder()
时,实际上是调用了代理对象的placeOrder
方法。 - 代理对象工作: 代理对象拦截了这个调用,先开启一个数据库事务,然后调用原始
OrderService
实例的placeOrder()
方法。原始方法执行完毕后,代理对象再根据执行结果(是否抛异常)来提交或回滚事务。
所以,BeanPostProcessor.postProcessAfterInitialization
就是 Spring AOP 插入其逻辑、创建代理对象并替换原始 Bean 实例的那个时机。通过这个机制,Spring 实现了在不修改业务代码的情况下,为 Bean 添加事务、安全等横切功能!!