1. Spring6 当中 Bean 的生命周期的详细解析:有五步,有七步,有十步
文章目录
每博一文案
“这个世界本来就是这样的,会认识形形色色的人,会遇到无法理解的恶意,
会感到失望,但是只要过去了,你就会发现,那些带着偏见自说自话的言论,
还有那些不能理解的恶意,都是在提醒我们不要成为那样的人。
或许会焦虑,会不知所措,生活也不太如意,会感到绝望。但我想说:前路漫漫,需自身强大。”
世界就是如此复杂,生活也从不是尽如人意,但我还是希望你,
即使见过那些肮脏与不堪,也依然能保有一颗温良的心。深渊可以凝视,但不要驻足。
只要心存光明,世界就不黑暗。
1.1 什么是 Bean 的生命周期
Spring 其实就是一个管理 Bean 对象的工厂。它负责对象的创建,对象的销毁等。
所谓的生命周期:简单的来说:就是一个对象从创建开始到最终销毁的整个过程。
- 什么时候创建Bean 对象 ?
- 创建 Bean 对象的前后会调用什么方法?
- Bean 对象什么时候销毁?
- Bean 对象的在销毁前后会调用生命方法?
那么我们为什么要知道 Bean 的生命周期呢?
1.2 Bean 的生命周期 “五步”
关于 Bean 生命周期的管理,我们可以参考Spring的源码的:AbstractAutowireCapableBeanFactory类的doCreateBean()方法。 想要进一步了解该源码内容的大家可以移步至✏️✏️✏️Spring6 当中的 Bean 循环依赖的详细处理方案+源码解析-CSDN博客
这里的 Bean的生命周期“五步”是最基本,比较粗略的五步。
注意点:
实践测试:
准备工作:配置导入 相关的 spring 框架,让 Maven 帮我们导入 spring的相关jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rainbowsea</groupId>
<artifactId>spring6-006-bean-lifecycle-blog</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.11</version>
</dependency>
<!-- junit4 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
第一步: 定义一个 Bean 对象类,这里我们就定义一个 User 的类,来进行测试。
package com.rainbowsea.bean;
public class User {
private String name;
public User() {
System.out.println(" 第一步: User 无参数构造方法调用");
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("第二步: 为 User 这个 Bean 进行赋值操作");
}
/**
* 这个方法需要自己写,方法名随意,但是注意写好之后,要通过init-method 配给Spring框架,让其知道这个东东
*/
public void initUserBean() {
System.out.println("第三步: 对 User Bean 对象进行一个初始化操作");
}
/**
* 这个方法需要自己写,方法名随意,但是注意写好之后,要通过destroy-method配给Spring框架,让其知道这个东东
*/
public void destroyUserBean() {
System.out.println("第五步: 对 User Bean 对象进行一个销毁操作");
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
第二步: 配置相关的 spring.xml
告诉 Spring 框架要做的事情。
<?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">
<!--init-method指明Bean的初始化方法是哪个;destroy-method指明Bean的销毁方法是哪个 -->
<bean id="userBean" class="com.rainbowsea.bean.User" init-method="initUserBean" destroy-method="destroyUserBean">
<property name="name" value="张三"></property> <!--set注入赋值-->
</bean>
</beans>
第三步: 运行客户端,模拟测试
注意点:
package com.rainbowsea.test;
import com.rainbowsea.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanLifecycleTest {
@Test
public void testRegisterBean() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring6.xml");
User userBean = applicationContext.getBean("userBean", User.class);
System.out.println("第四步: 使用 User Bean 对象" + userBean);
ClassPathXmlApplicationContext classPathXmlApplicationContext = (ClassPathXmlApplicationContext) applicationContext;
// 注意点:这里的 close()方法是,ClassPathXmlApplicationContext 类才有的,它的ApplicationContext 父类没有。
// 父类无法调用子类特有的方法,所以这里我们需要强制类型转换回来(向下转型),为子类
// 只有正常关闭spring容器才会执行销毁方法
classPathXmlApplicationContext.close();
}
}
执行结果:
总结注意点:
1.3 Bean 的生命周期 “七步”
Bean 的生命周期分为“七步”: 是在五步的当中的:第三步初始化Bean 的前后添加上的,Bean 的后处理器。如果加上 Bean 后处理器的话,Bean 的生命周期就是 七步了。
具体七步如下:
第一步:关于 Bean 的后处理器的编写:
关于: bean的后处理器的编写:
这里我们定义一个MyBeanPostProcessor 类作为 Bean 后处理器 实现该 implements BeanPostProcessor 接口,并重写其中的两个默认方法。
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// Object bean 是 该对应的 bean 的对象
// String beanName 是该对应 bean 的在配置文件当中 id
System.out.println("第三步: Bean 初始化之前执行before()方法");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// Object bean 是 该对应的 bean 的对象
// String beanName 是该对应 bean 的在配置文件当中 id
System.out.println("第五步: Bean 初始化之后执行after() 方法");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
第二步:将 Bean 后处理器配置到 spring.xml
文件当中去,告知 Spring 框架,同时进行管理。
第三步:模拟客户端,执行程序:
import com.rainbowsea.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanLifecycleTest {
@Test
public void testRegisterBean() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring6.xml");
User userBean = applicationContext.getBean("userBean", User.class);
System.out.println("第六步: 使用 User Bean 对象" + userBean);
ClassPathXmlApplicationContext classPathXmlApplicationContext = (ClassPathXmlApplicationContext) applicationContext;
// 注意点:这里的 close()方法是,ClassPathXmlApplicationContext 类才有的,它的ApplicationContext 父类没有。
// 父类无法调用子类特有的方法,所以这里我们需要强制类型转换回来(向下转型),为子类
// 只有正常关闭spring容器才会执行销毁方法
classPathXmlApplicationContext.close();
}
}
上面我们提到了**“这个 bean 后处理器的是作用于整个对应的 spring.xml 的配置文件上的所有的 Bean”** 的关于这一点,我们下面进行一个简单的验证一下。
我们再创建一个空的类,然后进行一个构造方法的注入,交给 Spring 框架进行管理,是否还是会执行 Bean 处理器。
运行测试:
1.4 Bean 的生命周期 “十步”
Bean的生命周期的“十步”就是更加的细化了 。更加的灵活了 。
让我们一起来看一下,这所谓的十步,是在上面的七步当中的哪些节点当中,增加了那另外的三步。
具体十步如下:
补充说明:
第一步:定义 Bean 类,我们还是使用这个 User 空白类,进行测试。
第二步:让 让User类实现5个接口(BeanNameAware,BeanClassLoaderAware,BeanFactoryAware,InitializingBean,DisposableBean),并实现其中的所有方法:
package com.rainbowsea.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class User implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
private String name;
public User() {
System.out.println("第一步: User 无参数构造方法调用");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("第五步:检查Bean是否实现了InitializingBean 接口,并调用接口方法.afterPropertiesSet执行");
}
@Override
public void destroy() throws Exception {
System.out.println("第九步: 检查 Bean是否实现了DisposableBean接口,并调用接口方法 destroy() ");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("第三步:检查是否实现了Aware的相关接口并调用其中的实现接口方法(): Bean 这个类的加载器" + classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("第三步:检查是否实现了Aware的相关接口并调用其中的实现接口方法(): 生产这个Bean的工厂对象是 " +beanFactory);
}
@Override
public void setBeanName(String name) {
System.out.println("第三步:检查是否实现了Aware的相关接口并调用其中的实现接口方法(): 这个Bean的名称是: " + name);
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("第二步: 为 User 这个 Bean 进行赋值操作");
}
/**
* 这个方法需要自己写,方法名随意,但是注意写好之后,要通过init-method 配给Spring框架,让其知道这个东东
*/
public void initUserBean() {
System.out.println("第六步: 对 User Bean 对象进行一个初始化操作");
}
/**
* 这个方法需要自己写,方法名随意,但是注意写好之后,要通过destroy-method配给Spring框架,让其知道这个东东
*/
public void destroyUserBean() {
System.out.println("第十步: 对 User Bean 对象进行一个销毁操作");
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
配置的 spring.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后处理器。这个后处理器将作用于当前配置文件中所有的bean。-->
<bean class="com.rainbowsea.bean.MyBeanPostProcessor"/>
<!--init-method指明Bean的初始化方法是哪个;destroy-method指明Bean的销毁方法是哪个 -->
<bean id="userBean" class="com.rainbowsea.bean.User" init-method="initUserBean" destroy-method="destroyUserBean">
<property name="name" value="张三"></property> <!--set注入赋值-->
</bean>
</beans>
对应的 Bean 后处理也是不变的(和生命周期七步是一样的),因为十步是基于七步的基础上细化,添加的。
package com.rainbowsea.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// Object bean 是 该对应的 bean 的对象
// String beanName 是该对应 bean 的在配置文件当中 id
System.out.println("第四步: Bean 初始化之前执行before()方法");
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// Object bean 是 该对应的 bean 的对象
// String beanName 是该对应 bean 的在配置文件当中 id
System.out.println("第七步: Bean 初始化之后执行after() 方法");
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
最后:运行测试:
2. Bean的作用域不同,管理方式也将是不同的
Spring 根据 Bean 的作用域来选择管理方式。
如下:我们把上述的User类的“Bean 的生命周期“十步”演示法当中的 ”spring.xml文件中的配置scope设置为prototype。其他任何内容保持不变。进行演示
运行测试:
2.1 自己new的对象让Spring管理
有些时候可能会遇到这样的需求,某个java对象是我们自己new的,然后我们希望这个对象被Spring容器管理,怎么实现?
准备工作,我们首先创建一个 bean 对象,这里我们创建一个空的 Vip 类,作为 bean 。
核心代码:
import com.rainbowsea.bean.User;
import com.rainbowsea.bean.Vip;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanLifecycleTest {
@Test
public void test() {
// 第一步: 我们自己 new 一个对象,方便交给 spring 管理
Vip vip = new Vip();
System.out.println(vip); // 打印一下地址,方便比较
// 第二步:将以上自己 new 的这个对象纳入 Spring 框架容器当中去管理,半路上交给 Spring来管理
// 通过 DefaultListableBeanFactory 这个对象,将我们 new 的对象交给 Spring 管理
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
// 通过: registerSingleton()方法,(这个交给 Spring 管理的bean的id/name 的命名,对于要交给spring管理的对象)
defaultListableBeanFactory.registerSingleton("vipBean",vip);
// 从Spring 容器中获取:通过上述我们 registerSingleton()方法中定义的id,进行一个获取
Object vipBean = defaultListableBeanFactory.getBean("vipBean");
System.out.println(vipBean);
// 单例:地址是一样的。
}
}
运行结果:
3. 总结:
- 熟悉Bean 的生命周期,各个时间节点上所能做的事情,可以让我们更加灵活的掌握Spring上的运用,更加的灵活,实现我们的业务需要。
- Bean 的生命周期 “五步”;注意点:初始化 Bean 需要我们通过在
spring.xml
配置文件当中的 标签当中通过init-method=
属性指定初始化方法 是哪一个方法;销毁 Bean 也是需要我们通过在spring.xml
配置文件当中的 标签当中的destroy-method=
性指定销毁方法 是哪个方法 - Bean 的生命周期分为“七步”;是在五步的当中的:第三步初始化Bean 的前后添加上的,Bean 的后处理器。如果加上 Bean 后处理器的话,Bean 的生命周期就是 七步了。该配置Bean后处理器。这个后处理器将作用于当前整个配置文件中所有的bean。
- Bean 的生命周期 “十步”;需要让类实现5个接口(BeanNameAware,BeanClassLoaderAware,BeanFactoryAware,InitializingBean,DisposableBean),并实现其中的所有方法:
- singleton (spring 默认的单例)作用域的 Bean ,Spring 能够精确地知道该 Bean 何时被创建,何时初始化完成的,以及何时被销毁的,符合上述的生命周期的“五步”,“七步”,“十步” 的流程。
- Bean的作用域不同,管理方式也将是不同的;而对于 prototype(多例) 作用域的 Bean,Spring 只负责创建,不符合上述的生命周期的“五步”,“七步”,“十步” 的流程。 singleton (spring 默认的单例)作用域的 Bean 符合上述的生命周期的“五步”,“七步”,“十步” 的流程。
- 自己new的对象让Spring管理(将以上自己 new 的这个对象纳入 Spring 框架容器当中去管理,半路上交给 Spring来管理);通过: DefaultListableBeanFactory 这个对象下的registerSingleton()方法,(这个交给 Spring 管理的bean的id/name 的命名,对于要交给spring管理的对象)