Spring中Bean的生命周期
# Spring中Bean的生命周期
# 什么是spring中的Bean
在 Spring 框架中,Bean 是由 Spring 容器管理的对象。
- 它本质上就是你写的一个普通 Java 对象(POJO),只不过交给了 Spring 容器来创建、初始化、销毁和管理依赖。
- Spring 通过配置(XML、Java 注解、Java 配置类等)来注册这些 Bean,然后在需要时从容器中获取实例。
简单来说:
- 你不需要手动
new
对象,Spring 会帮你创建并管理它们。 - Bean 是 Spring IoC(控制反转)的核心实现。
# 为什么要了解Bean的生命周期
Bean的生命周期由Spring IoC容器管理,从创建到销毁经历一系列阶段。
那我们为什么要了解它呢?
了解生命周期非常重要,尤其是做中高级开发和面试时。 掌握生命周期可以帮助你:
# 1.精确控制对象初始化
比如数据库连接池、缓存、日志组件需要在 Bean 初始化后做一些配置。
# 2.资源释放
你可以在 Bean 销毁前关闭线程池、数据库连接等资源。
# 3.调试和优化应用
知道什么时候 Bean 被创建和销毁,有助于解决内存泄漏、性能问题。
# 4.自定义逻辑
可以在生命周期的各个阶段插入自定义逻辑,比如通过 @PostConstruct
、InitializingBean
、DisposableBean
等。
简单说,就是我们可以更好的优化它(优化程序)或者增强bean的功能。
# Bean的生命周期
# 生命周期简洁流程
实例化 → 属性注入 → 初始化前(BeanPostProcessor#postProcessBeforeInitialization)
→ 初始化(@PostConstruct/afterPropertiesSet/init-method)
→ 初始化后(BeanPostProcessor#postProcessAfterInitialization)
→ Bean 准备就绪(供使用)
→ 容器关闭前(@PreDestroy/destroy-method)
→ Bean 销毁
2
3
4
5
6
# 主要方法/注解
- 构造方法:对象实例化时调用
- @Autowired/@Value:依赖注入
- @PostConstruct:初始化逻辑
- @PreDestroy:销毁逻辑
- InitializingBean/DisposableBean 接口:另一种初始化和销毁回调方式
# 详细版
# 1.容器启动:
- Spring IoC容器(ApplicationContext或BeanFactory)启动,开始加载Bean定义(通常来自XML文件、注解或Java配置)。
# 2.Bean定义加载:
- 容器解析Bean定义(BeanDefinition),包括Bean的类、作用域(scope)、依赖关系等信息。
# 3.Bean实例化(Instantiation):
- 容器通过调用Bean的构造函数创建Bean实例。
# 4.属性填充(Populate Properties):
- 容器通过setter方法或构造器注入依赖(Dependency Injection, DI),填充Bean的属性。
# 5.Aware接口回调:
- 如果Bean实现了Spring的Aware接口(如BeanNameAware、BeanFactoryAware、ApplicationContextAware),容器会调用相应方法注入相关信息。例如:
- BeanNameAware.setBeanName():设置Bean的名称。
- BeanFactoryAware.setBeanFactory():提供BeanFactory引用。
- ApplicationContextAware.setApplicationContext():提供ApplicationContext引用。
# 6.BeanPostProcessor前置处理(Pre-Initialization):
- 如果定义了BeanPostProcessor,其postProcessBeforeInitialization方法会在初始化回调之前调用,用于对Bean进行自定义处理。
# 7.初始化(Initialization):
- 在依赖注入完成后,Spring支持以下三种方式执行初始化逻辑:
- @PostConstruct注解:标记的方法在依赖注入完成后立即执行。
- InitializingBean接口:实现afterPropertiesSet()方法,执行初始化逻辑。
- 自定义init方法:通过XML的init-method属性或@Bean的initMethod指定自定义初始化方法。
- 如果同时使用多种方式,执行顺序为:@PostConstruct → InitializingBean.afterPropertiesSet() → 自定义init-method。
# 8.Bean就绪(Ready for Use):
- Bean初始化完成,进入就绪状态,可被应用程序使用。
# 9.销毁前处理(Pre-Destruction):
- 当容器关闭时(例如调用ConfigurableApplicationContext.close()),Spring支持以下方式执行销毁逻辑:
- @PreDestroy注解:标记的方法在Bean销毁前调用。
- DisposableBean接口:实现destroy()方法,执行销毁逻辑。
- 自定义destroy方法:通过XML的destroy-method属性或@Bean的destroyMethod指定自定义销毁方法。
- 执行顺序为:@PreDestroy → DisposableBean.destroy() → 自定义destroy-method。
# 10.Bean销毁(Destruction):
- Bean被移除,资源被释放,Bean从容器和JVM内存中销毁
# 流程图
容器启动
↓
【读取配置 & 注册 BeanDefinition】
↓
【调用 BeanFactoryPostProcessor】
↓
【实例化 Bean】(调用构造方法)
↓
【依赖注入】(@Autowired 等注入依赖)
↓
【BeanPostProcessor.postProcessBeforeInitialization】
↓
【初始化方法执行】
- @PostConstruct
- afterPropertiesSet()
- init-method
↓
【BeanPostProcessor.postProcessAfterInitialization】
↓
【Bean 可用】(正常业务逻辑)
↓
【容器关闭】
↓
【销毁方法执行】
- @PreDestroy
- destroy()
- destroy-method
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
关于依赖注入,这里,要提下@autowired 和 @Resource
@Autowired
- 作用:自动注入依赖对象。
- 原理:Spring 容器在创建 Bean 时,会在容器中查找匹配的依赖并注入。
- 用法:
- 可以用在构造器、字段、方法上。
- 默认是按类型注入,可结合
@Qualifier
精确指定 Bean。
# 生命周期管理-扩展
Spring的Bean生命周期设计是高度可扩展的,提供了多种机制让开发者在不同阶段干预Bean的行为。以下是对之前提到的三种生命周期管理方式是否为开发者扩展的说明:
# 1.接口方式(InitializingBean 和 DisposableBean):
- 扩展目的:通过实现InitializingBean.afterPropertiesSet()和DisposableBean.destroy(),开发者可以在Bean初始化和销毁阶段添加自定义逻辑。例如,初始化数据库连接或释放文件句柄。
- 用途:适合需要直接与Spring容器交互的场景,但由于直接依赖Spring接口,会导致代码与Spring框架耦合,不推荐频繁使用。
- 扩展性:开发者通过重写接口方法,可以完全控制初始化和销毁的逻辑,但扩展点较为单一,仅限于这两个阶段。
# 2.注解方式(@PostConstruct 和 @PreDestroy):
- 扩展目的:通过JSR-250标准的注解,开发者可以在Bean的初始化后和销毁前定义自定义方法,无需直接依赖Spring接口,解耦性更好。
- 用途:常用于初始化资源(如加载配置、建立连接)或清理资源(如关闭连接、释放内存)。这是现代Spring开发中最常用的扩展方式。
- 扩展性:注解方式简单直观,开发者只需在方法上添加注解即可,适合大多数场景,且易于维护。
# 3.XML或Java配置方式(init-method 和 destroy-method):
- 扩展目的:通过在XML的bean标签或@Bean注解中指定init-method和destroy-method,开发者可以定义任意名称的自定义方法来处理初始化和销毁逻辑。
- 用途:适合需要高度灵活性的场景,开发者可以自由定义方法名和逻辑,且完全不依赖Spring接口或注解。
- 扩展性:这种方式提供了最大的灵活性,开发者可以在不修改Bean类的情况下,通过配置文件调整生命周期行为。
- 优先选择注解:@PostConstruct和@PreDestroy是推荐的扩展方式,代码简洁且不耦合Spring。
- 避免混用多种方式:同时使用接口、注解和自定义方法可能导致逻辑重复或执行顺序混乱。
- 明确作用域:对于prototype作用域的Bean,Spring不会自动调用销毁方法,开发者需手动管理。
- 测试生命周期逻辑:在开发中,确保初始化和销毁逻辑正确执行,特别是在复杂依赖或多线程环境下。
# 扩展示例
# 示例1: 使用注解方式扩展初始化和销毁(@PostConstruct 和 @PreDestroy)
这个例子扩展一个数据库连接Bean,在初始化阶段建立连接,在销毁阶段关闭连接。适合资源管理场景。
代码示例:
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
@Component
public class DatabaseConnectionBean {
private Connection connection;
@PostConstruct
public void init() throws SQLException {
// 初始化逻辑:建立数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
System.out.println("数据库连接已建立");
}
@PreDestroy
public void cleanup() throws SQLException {
// 销毁逻辑:关闭数据库连接
if (connection != null && !connection.isClosed()) {
connection.close();
System.out.println("数据库连接已关闭");
}
}
// 其他业务方法...
public void executeQuery(String query) {
// 使用connection执行查询...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
实现的功能:
- 在Bean初始化后自动建立数据库连接,确保Bean就绪时资源可用。
- 在Bean销毁前自动关闭连接,防止资源泄漏。
- 这使得Bean可以安全地管理外部资源,如数据库、文件或网络连接。
# 示例2: 使用接口方式扩展初始化和销毁(InitializingBean 和 DisposableBean)
这个例子扩展一个缓存Bean,在初始化阶段加载缓存数据,在销毁阶段清空缓存。适合需要与Spring容器紧密集成的场景。
代码示例:
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class CacheBean implements InitializingBean, DisposableBean {
private Map<String, Object> cache = new HashMap<>();
@Override
public void afterPropertiesSet() throws Exception {
// 初始化逻辑:加载初始缓存数据
cache.put("key1", "value1");
cache.put("key2", "value2");
System.out.println("缓存已初始化,加载了初始数据");
}
@Override
public void destroy() throws Exception {
// 销毁逻辑:清空缓存
cache.clear();
System.out.println("缓存已清空");
}
// 其他业务方法...
public Object getFromCache(String key) {
return cache.get(key);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
实现的功能:
- 在Bean属性设置后自动加载初始缓存数据,确保Bean启动时缓存可用。
- 在容器关闭时自动清空缓存,释放内存并防止数据残留。
- 这使得Bean可以管理内部状态,如缓存、配置或临时数据。
扩展的点:
- 初始化阶段(After Properties Set):通过InitializingBean.afterPropertiesSet()在属性填充后执行自定义逻辑。
- 销毁阶段:通过DisposableBean.destroy()在Bean销毁时执行清理。
- 注意:这种方式直接依赖Spring接口,导致代码与框架耦合,不推荐作为首选,但适用于需要访问Spring内部资源的场景。
# 示例3: 使用BeanPostProcessor扩展初始化前后处理
这个例子扩展所有Bean的初始化过程,在初始化前后添加日志记录。BeanPostProcessor是一个更高级的扩展点,用于批量处理多个Bean。
代码示例:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 初始化前处理:记录日志
System.out.println("Before Initialization: Bean '" + beanName + "' is about to be initialized.");
return bean; // 返回原Bean或修改后的Bean
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 初始化后处理:记录日志
System.out.println("After Initialization: Bean '" + beanName + "' has been initialized.");
return bean;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
实现的功能:
- 在任何Bean初始化前记录日志,用于监控或调试。
- 在初始化后记录日志,确认Bean就绪。
- 这可以应用于AOP、性能监控或动态修改Bean属性(如添加代理)。
扩展的点:
- 预初始化阶段(Pre-Initialization):通过postProcessBeforeInitialization在初始化方法(如@PostConstruct)调用前扩展。
- 后初始化阶段(Post-Initialization):通过postProcessAfterInitialization在初始化方法调用后扩展。
- 优点:全局生效,可处理所有Bean,适合框架级扩展,如Spring AOP的实现基础。
# 示例3的扩展
在示例3中的BeanPostProcessor实现中,默认情况下它会应用于Spring容器中的所有Bean,因为BeanPostProcessor是全局性的扩展点。但你可以使它只对特定Bean生效,这是可行的。实现方式是通过在postProcessBeforeInitialization和postProcessAfterInitialization方法中添加条件判断,例如根据Bean的名称(beanName)或类类型(instanceof)来选择性地应用自定义逻辑。如果不匹配特定条件,则直接返回原Bean而不进行处理。
这种方式保持了BeanPostProcessor的单一实例,但通过内部逻辑实现针对性扩展,避免了不必要的全局影响。以下是详细说明和代码实现。
# 示例3的扩展-实现原理
为什么可行:BeanPostProcessor的两个方法会为每个Bean调用一次,你可以在方法开头检查当前Bean是否符合条件(如beanName等于"exampleBean"或bean属于特定类)。如果符合,则执行扩展逻辑(如日志记录、属性修改);否则,直接返回bean。
优点:简单高效,不需要创建多个BeanPostProcessor实例,逻辑集中在同一个类中。
适用场景:当你只想针对少数特定Bean添加行为时,例如只为数据库相关的Bean添加监控,或只为某些服务Bean注入额外属性。
注意事项:
- Bean名称(beanName)通常在配置文件或@Bean中定义。
- 如果需要更复杂的条件,可以结合bean.getClass()或注解检查(例如,如果Bean有特定注解,则应用)。
- 对于AOP或其他高级场景,Spring的@Aspect或Advisor可能更合适,但这里聚焦于BeanPostProcessor的针对性扩展。
# 示例3的扩展-代码示例
假设我们只想对名为"exampleBean"的Bean应用日志记录扩展。修改示例4的LoggingBeanPostProcessor类如下:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
private static final String TARGET_BEAN_NAME = "exampleBean"; // 指定目标Bean名称
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals(TARGET_BEAN_NAME)) {
// 只对特定Bean执行:初始化前处理
System.out.println("Before Initialization: Bean '" + beanName + "' is about to be initialized.");
}
return bean; // 无论是否匹配,都返回原Bean
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals(TARGET_BEAN_NAME)) {
// 只对特定Bean执行:初始化后处理
System.out.println("After Initialization: Bean '" + beanName + "' has been initialized.");
}
return bean;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
如果基于类类型判断(例如,只对ExampleBean类或其子类应用):
import com.example.ExampleBean; // 假设ExampleBean是目标类
// ... 其他代码同上 ...
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ExampleBean) {
System.out.println("Before Initialization: Bean '" + beanName + "' (type: ExampleBean) is about to be initialized.");
}
return bean;
}
// 同理修改postProcessAfterInitialization...
2
3
4
5
6
7
8
9
10
11
12
13
示例3-接口-处于的阶段
BeanPostProcessor 接口是 Spring 框架中一个重要的扩展点,它允许开发者在 Bean 的生命周期中插入自定义逻辑。具体来说,它发生在 Bean 的初始化阶段前后:
postProcessBeforeInitialization 方法:在 Bean 实例化、依赖注入(属性填充)完成后,但初始化方法(如 @PostConstruct、InitializingBean.afterPropertiesSet() 或自定义 init-method)调用之前执行。 这是一个预初始化阶段,用于在初始化前修改或包装 Bean。
postProcessAfterInitialization 方法:在所有初始化方法执行之后调用,但 Bean 就绪(Ready for Use)之前。 这是一个后初始化阶段,常用于 AOP 代理、日志记录或进一步自定义。