Appearance
Spring Bean 生命周期
在传统的 Java 应用中,Bean 的生命周期很简单,使用 Java 关键字 new 进行 Bean 的实例化后,这个 Bean 就可以使用了。一旦这个 Bean 长期不被使用,Java 自动进行垃圾回收。
相比之下,Spring 中 Bean 的生命周期较复杂,大致可以分为以下 5 个阶段:
- Bean 的实例化
- Bean 属性赋值
- Bean 的初始化
- Bean 的使用
- Bean 的销毁
Spring 根据 Bean 的作用域来选择 Bean 的管理方式,
- 对于 singleton 作用域的 Bean 来说,Spring IoC 容器能够精确地控制 Bean 何时被创建、何时初始化完成以及何时被销毁;
- 对于 prototype 作用域的 Bean 来说,Spring IoC 容器只负责创建,然后就将 Bean 的实例交给客户端代码管理,Spring IoC 容器将不再跟踪其生命周期。
Spring 生命周期流程
Spring Bean 的完整生命周期从创建 Spring IoC 容器开始,直到最终 Spring IoC 容器销毁 Bean 为止,其具体流程如下图所示。
图1:Spring Bean 生命周期流程
Bean 生命周期的整个执行过程描述如下。
- Spring 启动,查找并加载需要被 Spring 管理的 Bean,对 Bean 进行实例化。
- 对 Bean 进行属性注入。
- 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。
- 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。
- 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
- 如果 Bean 实现了 BeanPostProcessor 接口,则 Spring 调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。
- 如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。
- 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
- 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。
- 如果在
<bean>
中指定了该 Bean 的作用域为 singleton,则将该 Bean 放入 Spring IoC 的缓存池中,触发 Spring 对该 Bean 的生命周期管理;如果在<bean>
中指定了该 Bean 的作用域为 prototype,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。 - 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法销毁 Bean;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。
自定义 Bean 的生命周期
我们可以在 Spring Bean 生命周期的某个特定时刻,指定一些生命周期回调方法完成一些自定义的操作,对 Bean 的生命周期进行管理。
Bean 的生命周期回调方法主要有两种:
- 初始化回调方法:在 Spring Bean 被初始化后调用,执行一些自定义的回调操作。
- 销毁回调方法:在 Spring Bean 被销毁前调用,执行一些自定义的回调操作。
我们可以通过以下 3 种方式自定义 Bean 的生命周期回调方法:
- 通过接口实现
- 通过 XML 配置实现
- 使用注解实现
如果一个 Bean 中有多种生命周期回调方法时,优先级顺序为:注解 > 接口 > XML 配置。
通过接口实现
我们可以在 Spring Bean 的 Java 类中,通过实现 InitializingBean 和 DisposableBean 接口,指定 Bean 的生命周期回调方法。
回调方式 | 接口 | 方法 | 说明 |
---|---|---|---|
初始化回调 | InitializingBean | afterPropertiesSet() | 指定初始化回调方法,这个方法会在 Spring Bean 被初始化后被调用,执行一些自定义的回调操作。 |
销毁回调 | DisposableBean | destroy() | 指定销毁回调方法,这个方法会在 Spring Bean 被销毁前被调用,执行一些自定义的回调操作。 |
注意:通常情况下,我们不建议通过这种方式指定生命周期回调方法,这是由于这种方式会导致代码的耦合性过高。
示例
下面我们就通过一个实例,来演示下如何通过接口指定 Bean 的生命周期回调方法。
参考《第一个 Spring 程序》,新建一个名为 spring-life-cycle-demo 的 Java 项目。
在 net.biancheng.c 包下,创建一个名为 LifeCycleBean 的 Java 类,代码如下。
java
package net.biancheng.c;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
/***
* 使用接口,实现初始化回调 和销毁回调
*/
public class LifeCycleBean implements InitializingBean, DisposableBean {
private static final Log LOGGER = LogFactory.getLog(LifeCycleBean.class);
//网站名称
private String webName;
//网站地址
private String url;
public void setWebName(String webName) {
this.webName = webName;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return "LifeCycleBean{" +
"webName='" + webName + '\'' +
", url='" + url + '\'' +
'}';
}
/**
* 初始化回调逻辑
*
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
LOGGER.info(" 调用接口:InitializingBean,方法:afterPropertiesSet,无参数");
}
/**
* 销毁回调逻辑
*
* @throws Exception
*/
@Override
public void destroy() throws Exception {
LOGGER.info(" 调用接口:DisposableBean,方法:destroy,无参数");
}
}
- 在 src 目录下创建 Spring 配置文件 Beans.xml,配置如下。
xml
<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-3.0.xsd">
<bean id="lifeCycleBean" class="net.biancheng.c.LifeCycleBean">
<property name="webName" value="C语言中文网"></property>
<property name="url" value="c.biancheng.net"></property>
</bean>
</beans>
- 在 net.biancheng.c 包下,创建一个名为 MainApp 的类,代码如下。
java
package net.biancheng.c;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
private static final Log LOGGER = LogFactory.getLog(MainApp.class);
public static void main(String[] args) {
//获取 ClassPathXmlApplicationContext 容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
LifeCycleBean lifeCycleBean = context.getBean("lifeCycleBean", LifeCycleBean.class);
LOGGER.info(lifeCycleBean);
//手动销毁 Bean
context.close();
}
}
5. 运行 MainApp 类中的 main() 方法,控制台输出如下。
十二月 21, 2021 6:01:53 下午 net.biancheng.c.LifeCycleBean afterPropertiesSet
信息: 调用接口:InitializingBean,方法:afterPropertiesSet,无参数
十二月 21, 2021 6:01:53 下午 net.biancheng.c.MainApp main
信息: LifeCycleBean{webName='C语言中文网', url='c.biancheng.net'}
十二月 21, 2021 6:01:53 下午 net.biancheng.c.LifeCycleBean destroy
信息: 调用接口:DisposableBean,方法:destroy,无参数
通过 XML 配置实现
我们还可以在 Spring 的 XML 配置中,通过 <bean>
元素中的 init-method 和 destory-method 属性,指定 Bean 的生命周期回调方法。
XML 配置属性 | 描述 |
---|---|
init-method | 指定初始化回调方法,这个方法会在 Spring Bean 被初始化后被调用,执行一些自定义的回调操作。 |
destory-method | 指定销毁回调方法,这个方法会在 Spring Bean 被销毁前被调用,执行一些自定义的回调操作。 |
示例
下面我们通过一个实例,演示下如何通过 XML 配置指定 Bean 的生命周期回调方法。
参考《第一个 Spring 程序》,新建一个名为 spring-life-cycle-demo2 的 Java 项目。
在 net.biancheng.c 包下,创建一个名为 XMLLifeCycleBean 的 Java 类,代码如下。
java
package net.biancheng.c;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 通过 XML 配置指定生命周期回调方法
*/
public class XMLLifeCycleBean {
private static final Log LOGGER = LogFactory.getLog(XMLLifeCycleBean.class);
//网站名称
private String webName;
//网站地址
private String url;
public void setWebName(String webName) {
this.webName = webName;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public String toString() {
return "XMLLifeCycleBean{" +
"webName='" + webName + '\'' +
", url='" + url + '\'' +
'}';
}
/**
* 初始化回调方法
*/
public void init() {
LOGGER.info("在 XML 配置中通过 init-method 属性指定初始化方法:init() 方法");
}
/**
* 销毁回调方法
*/
public void destroy() {
LOGGER.info("在 XML 配置中通过 destroy-method 属性指定回调方法:destroy() 方法");
}
}
- 在 src 目录下,创建一个 Spring 配置文件 Beans.xml,配置内容如下。
xml
<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-3.0.xsd">
<!--通过 XML 配置指定生命周期回调方法-->
<bean id="xmlLifeCycleBean" class="net.biancheng.c.XMLLifeCycleBean" init-method="init" destroy-method="destroy">
<property name="webName" value="C语言中文网2"></property>
<property name="url" value="c.biancheng.net"></property>
</bean>
</beans>
- 在 net.biancheng.c 包下,创建一个名为 MainApp 的 Java 类,代码如下。
java
package net.biancheng.c;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
private static final Log LOGGER = LogFactory.getLog(MainApp.class);
public static void main(String[] args) {
//获取 ClassPathXmlApplicationContext 容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
XMLLifeCycleBean xmlLifeCycleBean = context.getBean("xmlLifeCycleBean", XMLLifeCycleBean.class);
LOGGER.info(xmlLifeCycleBean);
//手动销毁 Bean
context.close();
}
}
- 执行 MainApp 中的 main() 方法,控制台输出如下。
十二月 22, 2021 10:12:35 上午 net.biancheng.c.XMLLifeCycleBean init
信息: 在 XML 配置中通过 init-method 属性指定初始化方法:init() 方法
十二月 22, 2021 10:12:35 上午 net.biancheng.c.MainApp main
信息: XMLLifeCycleBean{webName='C语言中文网2', url='c.biancheng.net'}
十二月 22, 2021 10:12:35 上午 net.biancheng.c.XMLLifeCycleBean destroy
信息: 在 XML 配置中通过 destroy-method 属性指定销毁方法:destroy() 方法
使用注解实现
我们还可以通过 JSR-250 的 @PostConstruct 和 @PreDestroy 注解,指定 Bean 的生命周期回调方法。
注解 | 描述 |
---|---|
@PostConstruct | 指定初始化回调方法,这个方法会在 Spring Bean 被初始化后被调用,执行一些自定义的回调操作。 |
@PreDestroy | 指定销毁回调方法,这个方法会在 Spring Bean 被销毁前被调用,执行一些自定义的回调操作。 |
示例
下面我们通过一个实例,演示下如何通过注解指定 Bean 的生命周期回调方法。
参考《第一个 Spring 程序》,新建一个名为 spring-life-cycle-demo3 的 Java 项目。
在 net.biancheng.c 包下,创建一个名为 AnnotationLifeCycleBean 的 Java 类,代码如下。
java
package net.biancheng.c;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* 通过注解指定生命周期回调方法
*/
public class AnnotationLifeCycleBean {
private static final Log LOGGER = LogFactory.getLog(AnnotationLifeCycleBean.class);
//网站名称
private String webName;
//网站地址
private String url;
public AnnotationLifeCycleBean(String webName, String url) {
this.webName = webName;
this.url = url;
}
@Override
public String toString() {
return "AnnotationLifeCycleBean{" +
"webName='" + webName + '\'' +
", url='" + url + '\'' +
'}';
}
/**
* 初始化回调方法
*/
@PostConstruct
public void init() {
LOGGER.info("通过 @PostConstruct 注解,指定初始化方法:init() 方法");
}
/**
* 销毁回调方法
*/
@PreDestroy
public void destroy() {
LOGGER.info("通过 @PreDestroy 注解,指定初始化方法:destroy() 方法");
}
}
- 在 src 目录下,新建一个 Spring 配置文件 Beans.xml,配置内容如下。
xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--注解扫描-->
<context:component-scan base-package="net.biancheng.c"></context:component-scan>
<!--通过 XML 配置指定生命周期回调方法-->
<bean id="annotationLifeCycleBean" class="net.biancheng.c.AnnotationLifeCycleBean">
<constructor-arg name="webName" value="C语言中文网3"></constructor-arg>
<constructor-arg name="url" value="c.biancheng.net"></constructor-arg>
</bean>
</beans>
- 在 net.biancheng.c 包下,创建一个名为 MainApp 的 Java 类,代码如下。
java
package net.biancheng.c;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
private static final Log LOGGER = LogFactory.getLog(MainApp.class);
public static void main(String[] args) {
//获取 ClassPathXmlApplicationContext 容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
AnnotationLifeCycleBean annotationLifeCycleBean = context.getBean("annotationLifeCycleBean", AnnotationLifeCycleBean.class);
LOGGER.info(annotationLifeCycleBean);
//手动销毁 Bean
context.close();
}
}
- 执行 MainApp 中的 main 函数,控制台输出如下。
十二月 22, 2021 11:06:14 上午 net.biancheng.c.AnnotationLifeCycleBean init
信息: 通过 @PostConstruct 注解,指定初始化方法:init() 方法
十二月 22, 2021 11:06:14 上午 net.biancheng.c.MainApp main
信息: AnnotationLifeCycleBean{webName='C语言中文网3', url='c.biancheng.net'}
十二月 22, 2021 11:06:14 上午 net.biancheng.c.AnnotationLifeCycleBean destroy
信息: 通过 @PreDestroy 注解,指定初始化方法:destroy() 方法