Spring6 - Bean生命周期
# Bean 的生命周期五步
- 实例化 Bean:创建 Bean 的实例。
- Bean 属性赋值:为 Bean 注入属性和依赖。
- 初始化 Bean:执行自定义初始化方法或
@PostConstruct
注解方法。 - 使用 Bean:Bean 已准备好,可以被应用使用。
- 销毁 Bean:当应用或容器关闭时,执行销毁方法或
@PreDestroy
注解方法。
# Bean 生命周期七步:比五步添加的那两步在哪里?在初始化 Bean 的前和后。
- 实例化 Bean
- Bean 属性赋值
- 执行“Bean 后置处理器”的 before 方法:
BeanPostProcessor
的postProcessBeforeInitialization
方法执行。 - 初始化 Bean
- 执行“Bean 后置处理器”的 after 方法:
BeanPostProcessor
的postProcessAfterInitialization
方法执行。 - 使用 Bean
- 销毁 Bean
# Bean 生命周期十步:比七步添加的那三步在哪里?
- 点位1:在“Bean 后置处理器”before方法之前
- 干了什么事儿?
- 检查 Bean 是否实现了 Aware 相关的接口,如果实现了接口则调用这些接口中的方法。然后调用这些方法的目的是为了给你传递一些数据,让你更加方便使用。
- 干了什么事儿?
- 点位2:在“Bean 后置处理器”before方法之后
- 干了什么事儿?
- 检查 Bean 是否实现了 InitializingBean 接口,如果实现了,则调用接口中的方法。
- 干了什么事儿?
- 点位3:使用 Bean 之后,或者说销毁 Bean 之前
- 干了什么事儿?
- 检查 Bean 是否实现了 DisposableBean 接口,如果实现了,则调用接口中的方法。
- 干了什么事儿?
添加的这三个点位的特点:都是在检查你这个 Bean 是否实现了某些特定的接口,如果实现了这些接口,则 Spring 容器会调用这个接口中的方法。
# Bean 生命周期详细解释
Spring Bean 的生命周期是相对复杂的,因为它包含了多个阶段和回调接口,这使得开发者可以在 Bean 的生命周期的不同阶段插入自定义逻辑。下面是对每个阶段更详细的解释:
# 1. Bean 定义的注册
Spring 容器在启动时会首先加载并注册 Bean 的定义信息。这些定义信息可以来自于多个来源,包括 XML 配置文件、Java 配置类、注解等。在这个阶段,容器不会创建 Bean 的实例,只是将其定义信息(包括类类型、作用域、生命周期回调等)读入并进行注册。
# 2. Bean 的实例化
根据 Bean 的定义信息,Spring 容器会通过反射等机制创建 Bean 的实例。这个过程主要发生在两种类型的 Bean 之间:使用默认构造函数创建的 Bean,以及通过工厂方法创建的 Bean。如果 Bean 配置了懒加载(Lazy-Init),这个步骤会被延迟到首次获取 Bean 时进行。
提示
实例化指的是通过调用构造方法为对象在堆内存中分配空间的过程。此时,对象已经被创建,但是它的状态可能还不完整,因为它的属性可能还没有被设置。
在 Spring 中,Bean 的实例化可以通过几种方式进行:
1. 默认构造函数
当一个 Bean 定义没有指定工厂方法或构造函数参数时,Spring 会使用类的默认(无参)构造函数来创建 Bean 实例。这是最直接和常见的实例化方式。例如:
public class MyBean {
public MyBean() {
// 默认构造函数
}
}
2
3
4
5
2. 工厂方法
Spring 允许使用静态工厂方法或者工厂 Bean 的非静态方法来创建 Bean 实例。这种方式在创建对象时提供了更高的灵活性,特别是对于那些没有无参构造函数的类,或者在创建对象时需要执行额外逻辑的情况。
静态工厂方法:
public class MyBeanFactory {
public static MyBean createInstance() {
// 执行一些额外的逻辑
return new MyBean();
}
}
2
3
4
5
6
工厂 Bean 的方法:
public class MyBeanFactory {
public MyBean createInstance() {
// 执行一些额外的逻辑
return new MyBean();
}
}
2
3
4
5
6
3. 懒加载(Lazy-Init)
默认情况下,Spring 容器在启动时会创建和配置所有单例范围的 Bean。但是,你可以配置一个 Bean 为懒加载(lazy-init),这意味着该 Bean 在首次被请求时才会被创建和初始化,而不是在应用启动时。这样做可以减少应用启动时间,特别是对于大型应用来说非常有用。
懒加载的配置示例:
<bean id="myLazyBean" class="com.example.MyBean" lazy-init="true"/>
或者使用注解方式:
@Bean(lazyInit = true)
public MyBean myLazyBean() {
return new MyBean();
}
2
3
4
# 3. 设置 Bean 属性
创建 Bean 实例之后,Spring 容器会根据 Bean 定义信息中的属性声明,通过反射机制来注入属性。这包括对其他 Bean 的引用、基本类型值的注入等。
提示
在 Spring 框架中,设置 Bean 属性的几种主要方式包括直接在 XML 配置文件中使用 <property>
元素指定属性值,通过 Java 配置类中的 @Bean
方法配合构造器注入或设置器方法注入属性值,以及利用 @Autowired
注解实现自动装配,自动装配可以应用于字段、构造器、设置器方法,从而减少显式配置的需要,提高开发效率和代码的灵活性。
# 4. 实现各种 Aware 接口
Spring 容器提供了一系列的 Aware 接口,让 Bean 可以感知到容器的某些特定功能。BeanNameAware
、BeanFactoryAware
和 ApplicationContextAware
是其中最常用的几个。实现了这些接口的 Bean 在属性填充之后,会收到相应的回调,例如,一个 Bean 可以通过 BeanNameAware
获知自己在容器中的名称。
BeanNameAware
- 用途:允许一个 Bean 获知自己在 Spring 容器中的名称(或 ID)。
- 方法:
void setBeanName(String name)
- 场景:当你希望基于 Bean 的名称来执行特定逻辑,或者仅仅是为了记录目的,实现此接口会非常有用。
BeanFactoryAware
- 用途:允许一个 Bean 访问持有它的 Spring
BeanFactory
,即 Spring IoC 容器。 - 方法:
void setBeanFactory(BeanFactory beanFactory)
- 场景:如果你需要访问 Spring 容器,以获取其他 Bean 或查询容器的状态,实现此接口会很有帮助。它特别适用于需要大量操作 Spring 容器的 Bean。
ApplicationContextAware
- 用途:比
BeanFactoryAware
提供了更丰富的功能。允许访问当前的ApplicationContext
,从而让 Bean 能够访问应用级别的上下文环境,例如文件资源、发布事件等。 - 方法:
void setApplicationContext(ApplicationContext applicationContext)
- 场景:当你的 Bean 需要访问资源文件、发布应用事件或需要访问应用上下文的其他特定功能时,实现此接口很有用。
# 5. BeanPostProcessor 的前置处理
在 Bean 初始化之前,Spring 容器会调用所有注册的 BeanPostProcessor
的 postProcessBeforeInitialization
方法。这给了开发者一个机会在 Bean 属性设置完毕后、初始化前进行自定义逻辑处理。
# 6. InitializingBean 和自定义初始化方法
如果 Bean 实现了 InitializingBean
接口,容器会在所有属性设置之后调用 afterPropertiesSet()
方法。此外,如果 Bean 定义中指定了 init-method
,该方法也会在 afterPropertiesSet()
之后被调用。
# 7. BeanPostProcessor 的后置处理
Bean 初始化完成之后,容器会调用所有注册的 BeanPostProcessor
的 postProcessAfterInitialization
方法。这是对 Bean 进行最后修改的机会。
# 8. 使用 Bean
此时,Bean 已经完全初始化,可以被应用程序使用。
# 9. DisposableBean 和自定义销毁方法
当容器关闭时,如果 Bean 实现了 DisposableBean
接口,容器会调用 destroy()
方法。此外,如果 Bean 定义中指定了 destroy-method
,该方法也会被调用。这些方法提供了一个回调,允许 Bean 在容器关闭前进行必要的资源清理工作。
# 10. 容器关闭
这是 Bean 生命周期的最后阶段。Spring 容器会在关闭时逐个销毁单例模式的 Bean,按照依赖关系逆序执行销毁逻辑,确保依赖项在被依赖项之前销毁。
# 其他接口和功能
- 工厂方法与 FactoryBean:对于那些需要复杂创建逻辑的 Bean,可以通过实现
FactoryBean
接口或使用工厂方法来创建。 - 作用域:Spring 支持不同的 Bean 作用域,例如单例(Singleton)、原型(Prototype)、会话(Session)等,这会影响 Bean 的生命周期和管理方式。
- 生命周期注解:Spring 还支持通过
@PostConstruct
和@PreDestroy
注解来标注初始化和销毁方法,这为 Bean 生命周期管理提供了更灵活的方式。
# Bean 生命周期代码演示
# 1. 创建一个简单的 Bean 类
首先,我们定义一个名为 LifeCycleBean
的 Java 类,这个类会展示 Spring Bean 生命周期的关键阶段:
package com.example.lifecycle;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class LifeCycleBean implements InitializingBean, DisposableBean, ApplicationContextAware, BeanNameAware {
private String value;
// 构造函数 - 在Bean的创建过程中最先被调用,用于实例化Bean。
public LifeCycleBean() {
System.out.println("1. Bean 实例化");
}
// Spring依赖注入 - 通过反射机制将属性值注入到Bean的属性中。
public void setValue(String value) {
System.out.println("2. 属性值注入");
this.value = value;
}
// BeanNameAware接口 - 用于获取Bean的ID或名称,Spring容器会在Bean的属性设置完成后调用此方法。
@Override
public void setBeanName(String name) {
System.out.println("3. BeanNameAware接口方法调用,Bean 名称为: " + name);
}
// ApplicationContextAware接口 - 允许Bean获取Spring ApplicationContext,从而让Bean能够使用容器的服务。
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
System.out.println("4. ApplicationContextAware接口方法调用");
}
// @PostConstruct注解 - 标记自定义初始化方法,此方法在Bean的属性设置完成并且满足所有Bean的依赖之后被调用。
@PostConstruct
public void initMethod() {
System.out.println("5. @PostConstruct注解定义的自定义初始化方法");
}
// InitializingBean接口 - afterPropertiesSet方法在所有Bean的属性被设置后调用,也就是在@PostConstruct注解方法之后被调用。
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("6. InitializingBean接口方法调用--重写afterPropertiesSet方法进行初始化");
}
// 自定义初始化方法 - 可以在配置文件中通过init-method属性指定,也在Java配置中通过@Bean(initMethod="")指定。
public void customInitMethod() {
System.out.println("7. XML配置或Java配置中init-method属性定义的自定义初始化方法");
}
// @PreDestroy注解 - 标记自定义销毁方法,此方法在Bean销毁之前被调用,用于资源清理等。
@PreDestroy
public void preDestroy() {
System.out.println("8. @PreDestroy注解定义的自定义销毁方法");
}
// DisposableBean接口 - destroy方法在Bean销毁时调用,用于释放资源,这个方法在@PreDestroy注解方法之后调用。
@Override
public void destroy() throws Exception {
System.out.println("9. DisposableBean接口方法调用 -- 重写destroy方法自定义销毁逻辑");
}
// 自定义销毁方法 - 可以在配置文件中通过destroy-method属性指定,也在Java配置中通过@Bean(destroyMethod="")指定。
public void customDestroyMethod() {
System.out.println("10. XML配置或Java配置中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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
- 构造函数和属性注入发生在Bean实例化的初期阶段。
BeanNameAware
和ApplicationContextAware
提供了一种机制,让 Bean 在实例化过程中能够访问到容器的一些环境信息。@PostConstruct
和InitializingBean
的afterPropertiesSet
方法都用于初始化过程,但@PostConstruct
优先于afterPropertiesSet
执行。@PreDestroy
和DisposableBean
的destroy
方法都用于销毁过程,@PreDestroy
优先于destroy
执行。- 通过 XML 或 Java 配置指定的自定义初始化和销毁方法提供了更多的灵活性,允许在不修改Bean源代码的情况下,改变初始化和销毁的行为。
# 2. 配置 Bean 和 BeanPostProcessor
# 方式1:配置类实现
我们将通过 Java 配置类来定义 Bean 和 BeanPostProcessor。这个配置类注册了 LifeCycleBean
和自定义的 BeanPostProcessor
,以便在 Bean 的初始化之前和之后执行一些自定义逻辑。
package com.example.lifecycle;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
// 定义一个Bean,同时指定了自定义的初始化方法和销毁方法
@Bean(initMethod = "customInitMethod", destroyMethod = "customDestroyMethod")
public LifeCycleBean lifeCycleBean() {
// 返回LifeCycleBean的一个实例
return new LifeCycleBean();
}
// 定义一个自定义的BeanPostProcessor Bean,用于在Bean的初始化前后执行自定义逻辑
@Bean
public BeanPostProcessor customBeanPostProcessor() {
// 创建匿名内部类的实例
return new BeanPostProcessor() {
// 在Bean初始化之前调用
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
// 检查当前bean是否是LifeCycleBean的实例
if (bean instanceof LifeCycleBean) {
// 如果是,则输出信息
System.out.println("5.1 BeanPostProcessor接口的postProcessBeforeInitialization方法调用");
}
// 返回处理过的bean实例
return bean;
}
// 在Bean初始化之后调用
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
// 检查当前bean是否是LifeCycleBean的实例
if (bean instanceof LifeCycleBean) {
// 如果是,则输出信息
System.out.println("7.1 BeanPostProcessor接口的postProcessAfterInitialization方法调用");
}
// 返回处理过的bean实例
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 方式2:XML实现
使用 XML 配置来定义 Bean、自定义初始化和销毁方法,以及实现 BeanPostProcessor
,我们首先需要创建相应的 Java 类,然后在 XML 文件中进行配置。
1. 创建自定义 BeanPostProcessor 实现
package com.example.lifecycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class CustomBeanPostProcessor implements BeanPostProcessor {
@Override // 在Bean初始化之前调用该方法
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 如果当前正在初始化的Bean是LifeCycleBean实例
if (bean instanceof LifeCycleBean) {
// 执行自定义逻辑
System.out.println("BeanPostProcessor 的 postProcessBeforeInitialization 方法调用");
}
// 返回传入的Bean
return bean;
}
@Override // 在Bean初始化之后调用该方法
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 同样,检查Bean实例
if (bean instanceof LifeCycleBean) {
// 执行自定义逻辑
System.out.println("BeanPostProcessor 的 postProcessAfterInitialization 方法调用");
}
// 返回Bean实例
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
28
29
BeanPostProcessor 提供了两个回调方法,允许在 Bean 初始化的前后执行自定义逻辑:
postProcessBeforeInitialization
:此方法在任何 Bean 初始化回调(如设置属性后、自定义初始化方法或@PostConstruct
注解方法之前)之前被调用。可以用来对 Bean 进行预处理,例如修改 Bean 的属性。需要注意的是,如果这个方法返回了一个不同于传入的 Bean 实例,那么新的 Bean 实例将会替换原本的 Bean 实例继续进行生命周期的后续处理。postProcessAfterInitialization
:此方法在 Bean 初始化之后被调用,即在初始化回调(如自定义初始化方法或@PostConstruct
注解方法之后)之后。这个阶段是在所有初始化工作完成后,可以用来进行后处理,如代理包装。
2. XML 配置文件
在 XML 配置文件中,我们会定义 LifeCycleBean
Bean,并指定自定义的初始化和销毁方法。同时,我们也将注册自定义的 BeanPostProcessor
。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 定义 LifeCycleBean 并指定自定义的初始化和销毁方法-->
<bean id="lifeCycleBean" class="com.example.lifecycle.LifeCycleBean"
init-method="customInitMethod" destroy-method="customDestroyMethod">
</bean>
<!-- 注册 BeanPostProcessor -->
<bean class="com.example.lifecycle.CustomBeanPostProcessor"></bean>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3. 运行并观察生命周期
最后,创建一个主类来启动 Spring 应用上下文,并观察 LifeCycleBean
生命周期的各个阶段。
package com.example.lifecycle;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println("Spring 容器已启动,现在获取 LifeCycleBean...");
LifeCycleBean bean = context.getBean(LifeCycleBean.class);
System.out.println("LifeCycleBean 实例获取完毕。");
context.close();
System.out.println("Spring 容器关闭。");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
运行上述程序,将会在控制台看到如下输出(部分输出顺序可能因为 BeanPostProcessor 的配置不同而略有差异):
# ⾃⼰new的对象如何让Spring管理
通常,Spring 管理的 Bean 是由 Spring 容器通过解析配置文件(XML 或 Java Config)自动实例化和初始化的。但有时候,你可能会在应用中通过 new
操作符自己实例化对象,这些对象默认情况下不会被 Spring 管理。不过,有几种方法可以将这些自行实例化的对象纳入 Spring 管理:
# 1. 使用 @Configurable
注解
如果你想让通过 new
操作符创建的对象能够享受 Spring 的依赖注入等特性,可以考虑使用 AspectJ 的织入功能与 @Configurable
注解。这需要在编译时或加载时通过 AspectJ 来增强类,以便 Spring 可以在对象创建时自动注入依赖。
@Configurable
public class MyClass {
@Autowired
private MyDependency myDependency;
// 类的其他部分
}
2
3
4
5
6
7
这种方法需要额外的配置和编译步骤,包括在项目中加入 AspectJ 的依赖和配置编译器插件。
# 2. 手动注册对象到 Spring 上下文
另一种方法是在应用的运行时,将通过 new
创建的对象手动注册到 Spring 应用上下文中:
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
2
3
4
5
6
7
8
如果对象已经被创建,可以通过编程方式将其注册到 Spring 上下文中:
@Autowired
private ConfigurableApplicationContext applicationContext;
public void registerBeanDynamically() {
MyBean myBean = new MyBean();
applicationContext.getBeanFactory().registerSingleton("myBean", myBean);
}
2
3
4
5
6
7
# 3. 使用 @Bean
注解的方法
在配置类中,可以使用 @Bean
注解的方法来创建和注册 Bean。即使方法内部使用 new
来实例化对象,Spring 也会自动管理这些对象,包括执行依赖注入、调用初始化方法等。
@Configuration
public class MyConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
2
3
4
5
6
7
8
注意事项
直接使用 new
创建的对象,除非通过上述方法之一注册到 Spring 上下文中,否则它们不会享受到 Spring 提供的特性,如依赖注入、生命周期管理等。尽可能使用 Spring 的机制来创建和管理 Bean,以保持应用的一致性和可维护性。