前言
Spring中的bean都生存于Spring容器中,Spring容器负责初始化bean,装配bean和管理bean的生命周期。
Spring中的容器有两种。第一种是BeanFactory(org.springframework.beans.factory.BeanFactory)
,这是最简单的容器,提供基本的依赖注入功能;第二种是应用上下文ApplicationContext(org.springframework.context.ApplicationContext)
,基于BeanFactory
创建,提供应用框架级别的服务。
本篇文章将结合读书时期的一篇Spring学习笔记,重新温习Spring容器对bean的生命周期管理。
Spring版本:5.3.2
Springboot版本:2.4.1
正文
Spring的bean生命周期可以用下图进行概括。
Spring会负责bean整个生命周期,从初始化到销毁,本节将结合例子对bean的生命周期进行演示和说明,全篇不会涉及Spring源码和Springboot源码。演示工程的搭建只需要创建Maven工程并引入如下依赖。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<optional>true</optional>
</dependency>
</dependencies>
一. bean的初始化
bean的创建就是调用bean的构造方法将bean创建出来,本小节主要复习一个基础知识,即创建对象时类属性和对象属性的初始化顺序。
创建TestParentService
,如下所示。
@Slf4j
public class TestParentService {
private static String parentStaticVariable = "INIT PARENT STATIC VARIABLE";
private String parenObjectVariable = "INIT PARENT OBJECT VARIABLE";
static {
log.info("parentStaticVariable is " + parentStaticVariable);
parentStaticVariable = "PARENT STATIC VARIABLE";
log.info("parentStaticVariable is " + parentStaticVariable);
}
{
log.info("parenObjectVariable is " + parenObjectVariable);
parenObjectVariable = "PARENT OBJECT VARIABLE";
log.info("parenObjectVariable is " + parenObjectVariable);
}
public TestParentService() {
log.info("TestParentService construction method execute.");
}
}
再创建TestSonService
并继承TestParentService
,如下所示。
@Slf4j
public class TestSonService extends TestParentService {
private static String sonStaticVariable = "INIT SON STATIC VARIABLE";
private String sonObjectVariable = "INIT SON OBJECT VARIABLE";
static {
log.info("sonStaticVariable is " + sonStaticVariable);
sonStaticVariable = "SON STATIC VARIABLE";
log.info("sonStaticVariable is " + sonStaticVariable);
}
{
log.info("sonObjectVariable is " + sonObjectVariable);
sonObjectVariable = "SON OBJECT VARIABLE";
log.info("sonObjectVariable is " + sonObjectVariable);
}
public TestSonService() {
log.info("TestSonService construction method execute.");
}
}
在测试程序中创建一个TestSonService
的对象,并观察日志打印,测试代码如下所示。
public class TestClient {
public static void main(String[] args) {
TestSonService testSonService = new TestSonService();
}
}
日志打印结果如下所示。
上述日志打印可以得出创建对象时类属性和对象属性的初始化顺序,如下所示。
可以概括如下。
- 创建对象时,会先加载并初始化类;
- 如果类的父类未初始化,则会先初始化父类,再初始化子类;
- 初始化类时,先为静态变量赋初值,然后执行类的静态代码块;
- 类初始化后,会先初始化父类对象,然后再初始化子类对象;
- 初始化对象时,先为对象变量赋初值,然后执行类的普通代码块,最后执行构造方法。
二. BeanNameAware
bean通过实现BeanNameAware
接口可以获取bean在容器中的名字。定义一个TestService
,并实现BeanNameAware
接口,如下所示。
@Slf4j
public class TestService implements BeanNameAware {
private static String staticVariable = "INIT STATIC VARIABLE";
private String objectVariable = "INIT OBJECT VARIABLE";
static {
log.info("staticVariable is " + staticVariable);
staticVariable = "STATIC VARIABLE";
log.info("staticVariable is " + staticVariable);
}
{
log.info("objectVariable is " + objectVariable);
objectVariable = "OBJECT VARIABLE";
log.info("objectVariable is " + objectVariable);
}
public TestService() {
log.info("TestService construction method execute.");
}
@Override
public void setBeanName(String name) {
log.info("Bean name defined in context = " + name);
}
}
基于JavaConfig
的方式将TestService
的对象注册为容器中的bean,配置类BeanConfig
如下所示。
@Configuration
public class BeanConfig {
@Bean
public TestService testService() {
return new TestService();
}
}
测试代码如下所示。
public class TestClient {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class);
}
}
创建Spring容器时就会将TestService
的对象加载到容器中并开始管理其生命周期,日志打印如下所示。
bean实现了BeanNameAware
接口就可以通过setBeanName()
方法获取到bean在容器中的名字,并且setBeanName()
方法的调用时机在创建bean之后。
三. BeanFactoryAware
实现了BeanFactoryAware
接口的bean,可以获取到BeanFactory
的引用。修改TestService
并让其实现BeanFactoryAware
接口,如下所示。
@Slf4j
public class TestService implements BeanNameAware, BeanFactoryAware {
private static String staticVariable = "INIT STATIC VARIABLE";
private String objectVariable = "INIT OBJECT VARIABLE";
static {
log.info("staticVariable is " + staticVariable);
staticVariable = "STATIC VARIABLE";
log.info("staticVariable is " + staticVariable);
}
{
log.info("objectVariable is " + objectVariable);
objectVariable = "OBJECT VARIABLE";
log.info("objectVariable is " + objectVariable);
}
public TestService() {
log.info("TestService construction method execute.");
}
@Override
public void setBeanName(String name) {
log.info("Bean name defined in context = " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("testService is a singleton bean = " + beanFactory.isSingleton("testService"));
}
}
运行测试代码,日志打印如下所示。
bean实现了BeanFactoryAware
接口就可以通过setBeanFactory()
方法获取到BeanFactory
,从而可以实现判断bean的作用域等功能,setBeanFactory()
方法的调用时机在setBeanName()
方法之后。
四. ApplicationContextAware
实现了ApplicationContextAware
接口的bean,可以获取到ApplicationContext
的引用,通常可以通过ApplicationContext
来获取Spring容器管理的bean。修改TestService
并让其实现ApplicationContextAware
接口,如下所示。
@Slf4j
public class TestService implements
BeanNameAware, BeanFactoryAware, ApplicationContextAware {
private static String staticVariable = "INIT STATIC VARIABLE";
private String objectVariable = "INIT OBJECT VARIABLE";
static {
log.info("staticVariable is " + staticVariable);
staticVariable = "STATIC VARIABLE";
log.info("staticVariable is " + staticVariable);
}
{
log.info("objectVariable is " + objectVariable);
objectVariable = "OBJECT VARIABLE";
log.info("objectVariable is " + objectVariable);
}
public TestService() {
log.info("TestService construction method execute.");
}
@Override
public void setBeanName(String name) {
log.info("Bean name defined in context = " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("testService is a singleton bean = " + beanFactory.isSingleton("testService"));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("setApplicationContext method execute.");
}
}
运行测试代码,日志打印如下。
bean实现了ApplicationContextAware
接口就可以通过setApplicationContext()
方法获取到ApplicationContext
,从而通过ApplicationContext
获取Spring容器中的bean,setApplicationContext()
方法的调用时机在setBeanFactory()
方法之后。
五. BeanPostProcessor
在二到四小节中分析的接口均只能针对实现了接口的bean执行一些操作,如果需要对Spring容器中的所有bean执行一些操作,则需要提供一个BeanPostProcessor
接口的实现类。BeanPostProcessor
接口类图如下所示。
BeanPostProcessor
接口定义了两个default方法,BeanPostProcessor
接口的实现类可以选择对其进行重写以实现特定的业务逻辑。定义DecoratorService
类并实现BeanPostProcessor
接口,如下所示。
@Slf4j
public class DecoratorService implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof TestService) {
log.info("postProcessBeforeInitialization method execute.");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof TestService) {
log.info("postProcessAfterInitialization method execute.");
}
return bean;
}
}
修改BeanConfig
,将DecoratorService
的对象添加到Spring容器中,如下所示。
@Configuration
public class BeanConfig {
@Bean
public TestService testService() {
return new TestService();
}
@Bean
public DecoratorService decoratorService() {
return new DecoratorService();
}
}
执行测试代码,日志打印如下。
BeanPostProcessor
接口的实现类重写的postProcessBeforeInitialization()
方法会在setApplicationContext()
方法之后执行,postProcessAfterInitialization()
方法会在bean自定义的初始化方法之后执行。
六. InitializingBean
实现了InitializingBean
接口的bean,可以在实现的afterPropertiesSet()
方法中执行一些初始化逻辑。修改TestService
并使其实现InitializingBean
接口,如下所示。
@Slf4j
public class TestService implements
BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean {
private static String staticVariable = "INIT STATIC VARIABLE";
private String objectVariable = "INIT OBJECT VARIABLE";
static {
log.info("staticVariable is " + staticVariable);
staticVariable = "STATIC VARIABLE";
log.info("staticVariable is " + staticVariable);
}
{
log.info("objectVariable is " + objectVariable);
objectVariable = "OBJECT VARIABLE";
log.info("objectVariable is " + objectVariable);
}
public TestService() {
log.info("TestService construction method execute.");
}
@Override
public void setBeanName(String name) {
log.info("Bean name defined in context = " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("testService is a singleton bean = " + beanFactory.isSingleton("testService"));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("setApplicationContext method execute.");
}
@Override
public void afterPropertiesSet() {
log.info("afterPropertiesSet method execute.");
}
}
运行测试代码,日志打印如下。
可以知道afterPropertiesSet()
方法的执行时机在postProcessBeforeInitialization()
方法之后。
七. bean的自定义初始化方法
可以在类中自定义初始化方法,并在将对象注册到容器中时指定bean的自定义初始化方法。TestService
修改如下所示。
@Slf4j
public class TestService implements
BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean {
private static String staticVariable = "INIT STATIC VARIABLE";
private String objectVariable = "INIT OBJECT VARIABLE";
static {
log.info("staticVariable is " + staticVariable);
staticVariable = "STATIC VARIABLE";
log.info("staticVariable is " + staticVariable);
}
{
log.info("objectVariable is " + objectVariable);
objectVariable = "OBJECT VARIABLE";
log.info("objectVariable is " + objectVariable);
}
public TestService() {
log.info("TestService construction method execute.");
}
@Override
public void setBeanName(String name) {
log.info("Bean name defined in context = " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("testService is a singleton bean = " + beanFactory.isSingleton("testService"));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("setApplicationContext method execute.");
}
@Override
public void afterPropertiesSet() {
log.info("afterPropertiesSet method execute.");
}
public void initBean() {
log.info("initial method execute.");
}
}
修改BeanConfig
如下所示。
@Configuration
public class BeanConfig {
@Bean(initMethod = "initBean")
public TestService testService() {
return new TestService();
}
@Bean
public DecoratorService decoratorService() {
return new DecoratorService();
}
}
执行测试代码,日志打印如下所示。
可知bean自定义初始化方法的执行时机在afterPropertiesSet()
方法之后。
八. DisposableBean
实现了DisposableBean
接口的bean可以在bean销毁前执行一些操作。修改TestService
并实现DisposableBean
接口,如下所示。
@Slf4j
public class TestService implements
BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean,
DisposableBean {
private static String staticVariable = "INIT STATIC VARIABLE";
private String objectVariable = "INIT OBJECT VARIABLE";
static {
log.info("staticVariable is " + staticVariable);
staticVariable = "STATIC VARIABLE";
log.info("staticVariable is " + staticVariable);
}
{
log.info("objectVariable is " + objectVariable);
objectVariable = "OBJECT VARIABLE";
log.info("objectVariable is " + objectVariable);
}
public TestService() {
log.info("TestService construction method execute.");
}
@Override
public void setBeanName(String name) {
log.info("Bean name defined in context = " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("testService is a singleton bean = " + beanFactory.isSingleton("testService"));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("setApplicationContext method execute.");
}
@Override
public void afterPropertiesSet() {
log.info("afterPropertiesSet method execute.");
}
public void initBean() {
log.info("initial method execute.");
}
@Override
public void destroy() {
log.info("destroy method execute.");
}
}
修改测试代码,如下所示。
public class TestClient {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext(BeanConfig.class);
// 关闭容器,销毁bean
applicationContext.close();
}
}
运行测试代码,日志打印如下。
可知destroy()
方法的调用时机在postProcessAfterInitialization()
方法之后。
九. bean的自定义销毁方法
可以在类中自定义销毁方法,并在将对象注册到容器中时指定bean的自定义销毁方法。TestService
修改如下所示。
@Slf4j
public class TestService implements
BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean,
DisposableBean {
private static String staticVariable = "INIT STATIC VARIABLE";
private String objectVariable = "INIT OBJECT VARIABLE";
static {
log.info("staticVariable is " + staticVariable);
staticVariable = "STATIC VARIABLE";
log.info("staticVariable is " + staticVariable);
}
{
log.info("objectVariable is " + objectVariable);
objectVariable = "OBJECT VARIABLE";
log.info("objectVariable is " + objectVariable);
}
public TestService() {
log.info("TestService construction method execute.");
}
@Override
public void setBeanName(String name) {
log.info("Bean name defined in context = " + name);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("testService is a singleton bean = " + beanFactory.isSingleton("testService"));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("setApplicationContext method execute.");
}
@Override
public void afterPropertiesSet() {
log.info("afterPropertiesSet method execute.");
}
public void initBean() {
log.info("initial method execute.");
}
@Override
public void destroy() {
log.info("destroy method execute.");
}
public void destroyBean() {
log.info("destroy bean method execute.");
}
}
修改BeanConfig
,如下所示。
@Configuration
public class BeanConfig {
@Bean(initMethod = "initBean", destroyMethod = "destroyBean")
public TestService testService() {
return new TestService();
}
@Bean
public DecoratorService decoratorService() {
return new DecoratorService();
}
}
执行测试代码,日志打印如下。
可知bean自定义销毁方法的执行时机在destroy()
方法之后。
总结
Spring容器会负责容器内部的bean从创建到销毁。实现BeanNameAware
接口的bean可以获取到bean在容器中的名字;实现BeanFactoryAware
接口的bean可以获取到BeanFactory
的引用;实现ApplicationContextAware
接口的bean可以获取到ApplicationContext
的引用;实现InitializingBean
接口的bean可以在bean初始化之后做一些操作;实现DisposableBean
接口的bean可以在bean销毁之前做一些事情。除此之外,可以定义BeanPostProcessor
接口的实现类,其重写的方法会作用于容器中的每个bean。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。