目录
前提
一.Bean的完整周期
1.1什么是Bean的生命周期
二.SpringBean的五步分析法
2.1理论分析
2.2代码实现
三.Bean周期之七步分析法
3.1理论分析
3.2代码实现
四.Bean生命周期之十步分析法
4.1理论分析
4.2代码实现
五.总结
5.1五步、七步、十步的差别
5.2SpringBean周期的概况
前提
在学习Spring的对象注入之后,我们可以了解一下,Bean对象的生命周期,更好学习到在Spring容器里,Bean经历了什么!
ps:在日常里SrpingBean周期并不会使用,但是在面试当中经常出现!
一.Bean的完整周期
1.1什么是Bean的生命周期
在传统java应用程序里,bean是new进行实例化的,若不会使用了,则会由Java自动进行垃圾回收!
Spring Bean 的生命周期在不同的文献和说明中可能会被划分为不同的步骤,比如五步、七步或十步。这种不同的划分方式主要是为了更详细地描述 Bean 的生命周期过程,以便开发人员更好地理解和控制 Bean 的创建、初始化和销毁过程。
五步划分:通常指的是 BeanFactory 的生命周期,包括 BeanFactory 的实例化、配置元数据定位、Bean 的定义解析、Bean 的注册和实例化。这种划分方式主要关注 Spring 容器本身的初始化过程。
七步划分:在七步划分中,通常会将 BeanPostProcessor 的前置处理和后置处理分别作为两个独立的步骤,这样可以更清晰地表达 BeanPostProcessor 在 Bean 生命周期中的作用。
十步划分:这种划分方式可能会更加详细地描述 Bean 的初始化和销毁过程,包括 BeanFactory 的初始化、Bean 的实例化、设置属性、BeanPostProcessor 的前置处理、初始化方法调用、BeanPostProcessor 的后置处理、Bean 可使用阶段、销毁前处理、销毁方法调用和销毁后处理。这种划分方式提供了更加详细的生命周期过程描述,有助于开发人员深入理解 Bean 的各个阶段。
本文由浅到深,步步剖析!
二.SpringBean的五步分析法
2.1理论分析
所谓的⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,Bean的五步生命周期简略分为以下五部分:
- 实例化 Bean(为 Bean 分配内存空间,调用无参构造方法)
- 设置属性(Bean 注⼊和装配,调用set方法)
- Bean 初始化(会调用各种各样的方法,不需要仔细了解)
- 使⽤ Bean
- 销毁 Bean(Spring会自动调用destroy方法,当然也可以使用自己定义该方法)
图中,初始化和销毁Bean其实还需要实现几个接口、重写方法,但这是七步分析和十步分析当中学习,我们暂且不提,后期的七步分析和十步分析就是剖析这些接口方法。
- 实现了各种 Aware 通知的⽅法,如 BeanNameAware、BeanFactoryAware、 ApplicationContextAware 的接⼝⽅法;
- 执⾏ BeanPostProcessor 初始化前置⽅法;
- 执⾏ @PostConstruct 初始化⽅法,依赖注⼊操作之后被执⾏;
- 执⾏⾃⼰指定的 init-method ⽅法(该方法自己定义)
- 执⾏ BeanPostProcessor 初始化后置⽅法。
2.2代码实现
在Spring容器里,会自动管理Bean的初始化、设置属性、使用、销毁各个阶段,当Spring容器销毁时,会自动销毁容器内所有的Bean,会自动调用Bean的destroy方法,但有两种情况不会调用。
- 如果Bean类实现了DisposableBean接口,容器会调用其destroy()方法
- 如果在Spring配置文件中配置了destroy-method属性,容器会调用指定的销毁方法。
接下来的代码我们使用第二种方式,将具体流程呈现:
User类:
public class User {
private String username;
public void setUsername(String username) {
System.out.println("第二步:给对象的属性赋值");
this.username = username;
}
public User() {
System.out.println("第一步:实例化Bean,无参数构造方法执行了");
}
// 初始化Bean,为了呈现流程,自己写
public void initBean(){
System.out.println("第三步:初始化Bean");
}
// 销毁Bean,为了呈现流程,自己定义
public void destroyBean(){
System.out.println("第五步:销毁Bean");
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
'}';
}
}
xml文件配置:
<?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 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="user" class="com.school_ee.test.User"
init-method="initBean" destroy-method="destroyBean">
<!-- initBean和destroyBean是User类中自己定义的方法,会自动调用 -->
<!--给属性赋值-->
<property name="username" value="张三"/>
</bean>
</beans>
接下来测试类测试:
public void init(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Spring.xml");
User user = applicationContext.getBean("user", User.class);
System.out.println("第四步:使用Bean");
//为了呈现关闭流程、手动关闭Spring容器
ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
context.close();
}
执行结果:
注意点:
-
只有正常关闭spring容器(调用close方法),bean的销毁方法才会被调用
-
ClassPathXmlApplicationContext类才有close()方法,ApplicationContext中没有。
-
如果不希望Spring自动创建和销毁,可使配置文件中的init-method指定初始化方法,destroy-method指定销毁方法。
三.Bean周期之七步分析法
3.1理论分析
所谓的七步分析,是在五步法的Bean初始化阶段将 BeanPostProcessor接口 的前置处理和后置处理分别作为两个独立的步骤。就是更加清晰的表达BeanPostProcessor 在 Bean 生命周期中的作用。
BeanPostProcessor 接口的作用:Spring 框架提供的一个扩展点,用于在 Spring 容器实例化 Bean 和将 Bean 实例化到容器中之间的过程中,对 Bean 进行额外的处理。定义了前置处理方法和后置处理方法:
-
前置处理(postProcessBeforeInitialization):
- 在 Bean 实例化后、依赖注入后,以及自定义初始化方法调用之前执行。
- 可以用于对 Bean 进行额外的初始化操作,或者修改 Bean 的属性值。
- 常见的使用场景包括对 Bean 进行动态代理、添加日志记录、进行安全检查等。
-
后置处理(postProcessAfterInitialization):
- 在 Bean 自定义初始化方法调用后执行,也就是 Bean 已经完全初始化完成后执行。
- 可以用于对 Bean 进行额外的操作,或者对已初始化的 Bean 进行后续处理。
- 常见的使用场景包括对 Bean 进行缓存、注册监听器、执行特定的业务逻辑等。
3.2代码实现
为了更好展示流程,我们自定义类实现 BeanPostProcessor 接口,并重写其中的postProcessBeforeInitialization方法和postProcessAfterInitialization方法。
自定义类实现接口:
package com.bjpowernode.spring.test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class LogBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean初始化前,处理器的before方法执行");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化结束,处理器的after方法执行");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
xml配置:
<?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">
<bean id="user" class="com.school_ee.test.User"
init-method="initBean" destroy-method="destroyBean">
<!-- initBean和destroyBean是User类中自己定义的方法,会自动调用 -->
<!--给属性赋值-->
<property name="username" value="张三"/>
</bean>
<!--配置Bean后处理器。这个后处理器将作用于当前配置文件中所有的bean。-->
<bean class="com.school_ee.test.LogBeanPostProcessor" />
</beans>
测试文件不需要改变,直接上结果:
从测试结果中,我们可以发现,的的确确从初始化前后执行Before方法和after方法。
四.Bean生命周期之十步分析法
4.1理论分析
和七步分析做对比,额外的三步:
- 检查Bean是否实现了Aware相关的接口
- BeanNameAware 接口回调 ,目的:允许 bean 在初始化阶段获取自身在 Spring 容器中的名字
- BeanFactoryAware 接口回调,目的:允许 bean 在初始化阶段获取对 Spring 容器的引用
- 检查Bean是否实现了InitializingBean接口,Spring 在 bean 初始化完成后会自动调用
afterPropertiesSet()
方法,执行一些数据初始化、资源加载、连接数据库等必要操作- 检查Bean是否实现了DisposableBean接口,Spring 容器在销毁该 bean 时会调用其
destroy()
方法,从而允许 bean 执行一些资源释放或者清理操作。结论:检查你这个Bean是否实现了某些特定的接口,如果实现了这些接口,则Spring容器会调用这个接口中的方法!
4.2代码实现
user类实现所有接口的代码:
package com.bjpowernode.spring.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
public class User implements
BeanNameAware, BeanClassLoaderAware, BeanFactoryAware,
InitializingBean, DisposableBean{
private String username;
public void setUsername(String username) {
System.out.println("第二步:给对象的属性赋值");
this.username = username;
}
public User() {
System.out.println("第一步:实例化Bean,无参数构造方法执行了");
}
// 初始化Bean,需要自己写,自己配,方法名随意
public void initBean(){
System.out.println("第四步:初始化Bean");
}
// 销毁Bean,需要自己写,自己配,方法名随意
public void destroyBean(){
System.out.println("第七步:销毁Bean");
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
'}';
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("类加载器:" + classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("Bean工厂:" + beanFactory);
}
@Override
public void setBeanName(String name) {
System.out.println("Bean的名字:" + name);
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean destroy方法执行了");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean afterPropertiesSet方法执行了");
}
}
xml配置文件不变,测试类不变,直接测试:
五.总结
5.1五步、七步、十步的差别
标准的五步:实例化--->设置属性--->初始化---->使用Bean---->销毁Bean
七步:实例化--->设置属性--->前置处理----->初始化----->后置处理---->使用Bean---->销毁Bean
十步:实例化--->设置属性--->Aware接口实现--->前置处理----->InitializingBean接口--->初始化----->后置处理---->使用Bean---->DisposableBean接口---->销毁Bean
5.2SpringBean周期的概况
(1)Spring其实就是一个管理Bean对象的工厂,它负责对象的创建,对象的销毁等。
(2)所谓的生命周期就是:对象从创建开始到最终销毁的整个过程。
(3)为什么要知道Bean的生命周期?
定制 Bean 的初始化和销毁过程:通过了解 Bean 的生命周期,开发人员可以在 Bean 初始化之前或销毁之后执行特定的操作,比如资源的初始化和释放、日志记录、权限检查等。
解决依赖关系问题:在了解 Bean 的生命周期的过程中,可以更好地理解 Spring 容器是如何管理 Bean 的创建、依赖注入以及销毁的,有助于解决 Bean 之间的依赖关系问题。
优化性能:了解 Bean 的生命周期可以帮助开发人员避免一些不必要的操作,从而提高系统的性能。比如在 Bean 的初始化阶段进行一些耗时操作,可能会影响系统的启动速度。
排查问题:当出现一些意外情况或 Bug 时,了解 Bean 的生命周期可以帮助开发人员更快地定位问题所在,从而更容易地进行排查和修复。
注:上述的Bean周期了解即可,在Spring容器里已经实现!