Spring Bean 的生命周期

前言

本文主要介绍 Spring IoC 容器如何管理 Bean 的生命周期。
在应用开发中,常常需要执行一些特定的初始化工作,这些工作都是相对比较固定的,比如建立数据库连接,打开网络连接等,同时,在结束服务时,也有一些相对固定的销毁工作需要执行。为了便于这些工作的设计,Spring IoC 容器提供了相关的功能,可以让应用定制 Bean 的初始化和销毁过程。

Bean 生命周期

在这里插入图片描述
Bean 的生命周期可分为初始化阶段和销毁阶段

初始化阶段

  • 启动 Spring IoC 容器

  • 实例化
    调用 Bean 的构造方法创建一个对象,这个对象默认是单例的。

  • 设置属性
    调用 Bean 对象的 setter 方法设置属性值。

  • Aware 相关的属性,注入到 Bean 对象

    • 设置 Bean 名称
      如果 Bean 实现了 BeanNameAware 接口,调用其重写的 #setBeanName(String name) 方法,将 bean 标签中的 id 或 @Bean 注解的 name 属性(若没设置 name,则默认为方法名)设置为 Bean 的名称。
    • 设置 Bean 工厂
      如果 Bean 实现了 BeanFactoryAware 接口,调用其重写的 #setBeanFactory(BeanFactory factory) 方法,设置 Bean 工厂。
    • 设置上下文
      调用 ApplicationContextAware#setApplicationContext(ApplicationContext context) 方法。
  • 调用其他接口的方法,进一步初始化 Bean 对象

    • 如果存在与 Bean 关联的任何 BeanPostProcessor 实现类,则调用 #preProcessBeforeInitialization(Object bean, String beanName) 方法。
    • 如果 Bean 实现了 InitializingBean 接口,则会调用 #afterPropertiesSet() 方法。
      如果为 Bean 指定了 init 方法(例如 <bean />init-method 属性或 @Bean 注解的 initMethod 属性),那么将调用该方法。
    • 如果存在与 Bean 关联的任何 BeanPostProcessor 实现类,则将调用 #postProcessAfterInitialization(Object bean, String beanName) 方法。

销毁阶段

  • 如果 Bean 实现了 DisposableBean 接口,当 Spring 容器关闭时,会调用 #destroy() 方法。
  • 如果为 Bean 指定了 destroy 方法(例如 <bean />destroy-method 属性或 @Bean 注解的 destroyMethod 属性),那么将调用该方法。

代码实例

自定义 Bean

@Data
@Slf4j
public class Person implements BeanNameAware, BeanFactoryAware, InitializingBean, ApplicationContextAware, DisposableBean {

    private String name;

    public Person() {
        log.info("constructor: Instantiate");
    }

    public void setName(String name) {
        log.info("setter: populate property");
        this.name = name;
    }

    public String getName() {
        log.info("getter");
        return name;
    }

    @Override
    public void setBeanName(String beanName) {
        log.info("setBeanName: " + beanName);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("setBeanFactory");
    }

    @Override
    public void afterPropertiesSet() {
        log.info("afterPropertiesSet");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("setApplicationContext");
    }

    @Override
    public void destroy() {
        log.info("destroy");
    }

    public void customInit() {
        log.info("customInit");
    }

    public void customDestroy() {
        log.info("customDestroy");
    }
}

代码中自定义了一个 Bean 类 Person,实现以下接口并重写其中的方法:

interface method
BeanNameAware setBeanName
BeanFactoryAware setBeanFactory
InitializingBean afterPropertiesSet
ApplicationContextAware setApplicationContext
DisposableBean destroy

另外,为 Person 加入无参构造方法、getter/setter 方法。对这些方法全部加入日志,将当前方法名打印出来,便于在控制台观察顺序。

配置 Bean

@Configuration
public class PersonConfig {

    @Bean(initMethod = "customInit", destroyMethod = "customDestroy")
    public Person singletonPerson() {
        Person singletonPerson = new Person();
        singletonPerson.setName("Singleton Jake");
        return singletonPerson;
    }

    @Bean(initMethod = "customInit", destroyMethod = "customDestroy")
    @Lazy
    public Person lazySingletonPerson() {
        Person lazySingletonPerson= new Person();
        lazySingletonPerson.setName("Lazy Singleton Jake");
        return lazySingletonPerson;
    }

    @Bean(initMethod = "customInit", destroyMethod = "customDestroy")
    @Scope("prototype")
    public Person prototypePerson() {
        Person prototypePerson = new Person();
        prototypePerson.setName("Prototype Jake");
        return prototypePerson;
    }
}

上述代码为注册了三个 bean,则 IoC 容器中会存在三个类型相同,名称和作用范围不同的 bean。

classType beanName scope
1 Person singletonPerson singleton
2 Person lazySingletonPerson singleton
3 Person prototypePerson prototype

自定义 BeanPostProcessor

@Component
@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Person) {
            log.info("postProcessBeforeInitialization, beanName = " + beanName);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Person) {
            log.info("postProcessAfterInitialization, beanName = " + beanName);
        }
        return bean;
    }
}

BeanPostProcessor 允许 IoC 容器在 bean 初始化前后进行处理

启动 Application

注意:想观察 IoC 容器关闭(Bean 销毁)后的打印内容,在 pom 文件中不要加上 spring-boot-starter-web 依赖。
运行 Application 类中的 main 方法,控制台的打印如下(仅截取关键部分):

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.4.RELEASE)

2020-03-02 17:41:22.111  INFO 406860 --- [           main] c.j.s.l.SpringLifecycleApplication       : Starting SpringLifecycleApplication on V12CNSSZ01MGRPE with PID 406860 (C:\wengzhengkai\idea-projects\spring-lifecycle\target\classes started by 60055807 in C:\wengzhengkai\idea-projects\spring-lifecycle)
2020-03-02 17:41:22.117  INFO 406860 --- [           main] c.j.s.l.SpringLifecycleApplication       : No active profile set, falling back to default profiles: default
2020-03-02 17:41:23.068  INFO 406860 --- [           main] com.jake.spring.lifecycle.domain.Person  : constructor: Instantiate
2020-03-02 17:41:23.068  INFO 406860 --- [           main] com.jake.spring.lifecycle.domain.Person  : setter: populate property
2020-03-02 17:41:23.069  INFO 406860 --- [           main] com.jake.spring.lifecycle.domain.Person  : setBeanName: singletonPerson
2020-03-02 17:41:23.069  INFO 406860 --- [           main] com.jake.spring.lifecycle.domain.Person  : setBeanFactory
2020-03-02 17:41:23.069  INFO 406860 --- [           main] com.jake.spring.lifecycle.domain.Person  : setApplicationContext
2020-03-02 17:41:23.069  INFO 406860 --- [           main] c.j.s.l.processor.MyBeanPostProcessor    : postProcessBeforeInitialization, beanName = singletonPerson
2020-03-02 17:41:23.069  INFO 406860 --- [           main] com.jake.spring.lifecycle.domain.Person  : afterPropertiesSet
2020-03-02 17:41:23.070  INFO 406860 --- [           main] com.jake.spring.lifecycle.domain.Person  : customInit
2020-03-02 17:41:23.070  INFO 406860 --- [           main] c.j.s.l.processor.MyBeanPostProcessor    : postProcessAfterInitialization, beanName = singletonPerson
2020-03-02 17:41:23.201  INFO 406860 --- [           main] c.j.s.l.SpringLifecycleApplication       : Started SpringLifecycleApplication in 1.921 seconds (JVM running for 3.17)
2020-03-02 17:41:23.209  INFO 406860 --- [extShutdownHook] com.jake.spring.lifecycle.domain.Person  : destroy
2020-03-02 17:41:23.209  INFO 406860 --- [extShutdownHook] com.jake.spring.lifecycle.domain.Person  : customDestroy

打印顺序总结如下:

  • 容器启动后

    • Person#Person() 实例化一个对象
    • Person#setName(String name)设置属性 name 的值
    • Person#setBeanName(String beanName) 设置 beanName,方法继承自 BeanNameAware 接口
    • Person#setBeanFactory(BeanFactory beanFactory) 设置 beanFactory,方法继承自 BeanFactoryAware 接口
    • Person#setApplicationContext(ApplicationContext context) 设置 applicationContext,方法继承自 ApplicationContextAware 接口
    • MyBeanPostProcessor#postProcessBeforeInitialization(Object bean, String beanName) 初始化前的后置处理,可以猜想后置的含义就是实例化和属性设置之后。
    • Person#afterPropertiesSet() 此时属性值已经被设置完毕,方法继承自 InitializingBean 接口
    • Person#customInit() 自定义初始化方法。
  • 容器关闭后

    • Person#destroy() 销毁 bean 之前的操作,方法继承自 DisposableBean 接口
    • Person#customDestroy() 自定义的销毁 bean 之前的操作

由打印内容 setBeanName: singletonPerson , postProcessBeforeInitialization, beanName = singletonPerson, postProcessAfterInitialization, beanName = singletonPerson 可知,只有 beanName 为 singletonPerson 的 Bean 才在 Spring 容器启动时被初始化。

单元测试

编写单元测试,注入ApplicationContext 对象,使用工厂方法 getBean(String beanName) 获取 PersonConfig 中注册的三种 Bean 实例。

@SpringBootTest
class SpringLifecycleApplicationTests {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    void getSingletonBean() {
        Person singletonPerson = (Person) applicationContext.getBean("singletonPerson");
        assertNotNull(singletonPerson);
        assertSame("Singleton Jake", singletonPerson.getName());
        assertSame(singletonPerson, applicationContext.getBean("singletonPerson"));
    }

    @Test
    void getLazySingletonBean() {
        Person lazySingletonPerson = (Person) applicationContext.getBean("lazySingletonPerson");
        assertNotNull(lazySingletonPerson);
        assertSame("Lazy Singleton Jake", lazySingletonPerson.getName());
        assertSame(lazySingletonPerson, applicationContext.getBean("lazySingletonPerson"));
    }

    @Test
    void getPrototypeBean() {
        Person prototypePerson = (Person) applicationContext.getBean("prototypePerson");
        assertNotNull(prototypePerson);
        assertSame("Prototype Jake", prototypePerson.getName());
        assertNotSame(prototypePerson, applicationContext.getBean("prototypePerson"));
    }

}

运行单元测试类,在控制台中输出结果如下:

  • 容器启动部分

      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.2.4.RELEASE)
    
    2020-03-03 11:42:25.950  INFO 312788 --- [           main] c.j.s.l.SpringLifecycleApplicationTests  : Starting SpringLifecycleApplicationTests on V12CNSSZ01MGRPE with PID 312788 (started by 60055807 in C:\wengzhengkai\idea-projects\spring-lifecycle)
    2020-03-03 11:42:25.952  INFO 312788 --- [           main] c.j.s.l.SpringLifecycleApplicationTests  : No active profile set, falling back to default profiles: default
    2020-03-03 11:42:26.626  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : constructor: Instantiate
    2020-03-03 11:42:26.627  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setter: populate property
    2020-03-03 11:42:26.639  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setBeanName: singletonPerson
    2020-03-03 11:42:26.639  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setBeanFactory
    2020-03-03 11:42:26.639  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setApplicationContext
    2020-03-03 11:42:26.639  INFO 312788 --- [           main] c.j.s.l.processor.MyBeanPostProcessor    : postProcessBeforeInitialization, beanName = singletonPerson
    2020-03-03 11:42:26.639  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : afterPropertiesSet
    2020-03-03 11:42:26.640  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : customInit
    2020-03-03 11:42:26.640  INFO 312788 --- [           main] c.j.s.l.processor.MyBeanPostProcessor    : postProcessAfterInitialization, beanName = singletonPerson
    2020-03-03 11:42:26.783  INFO 312788 --- [           main] c.j.s.l.SpringLifecycleApplicationTests  : Started SpringLifecycleApplicationTests in 1.259 seconds (JVM running for 3.759)	
    
  • getSingletonBean

    2020-03-03 11:42:27.208  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : getter
    
  • getLazySingletonBean

    2020-03-03 11:42:27.236  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : constructor: Instantiate
    2020-03-03 11:42:27.236  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setter: populate property
    2020-03-03 11:42:27.236  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setBeanName: lazySingletonPerson
    2020-03-03 11:42:27.236  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setBeanFactory
    2020-03-03 11:42:27.236  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setApplicationContext
    2020-03-03 11:42:27.236  INFO 312788 --- [           main] c.j.s.l.processor.MyBeanPostProcessor    : postProcessBeforeInitialization, beanName = lazySingletonPerson
    2020-03-03 11:42:27.236  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : afterPropertiesSet
    2020-03-03 11:42:27.236  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : customInit
    2020-03-03 11:42:27.237  INFO 312788 --- [           main] c.j.s.l.processor.MyBeanPostProcessor    : postProcessAfterInitialization, beanName = lazySingletonPerson
    2020-03-03 11:42:27.237  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : getter
    
  • getPrototype

    2020-03-03 11:42:27.227  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : constructor: Instantiate
    2020-03-03 11:42:27.227  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setter: populate property
    2020-03-03 11:42:27.227  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setBeanName: prototypePerson
    2020-03-03 11:42:27.227  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setBeanFactory
    2020-03-03 11:42:27.227  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setApplicationContext
    2020-03-03 11:42:27.228  INFO 312788 --- [           main] c.j.s.l.processor.MyBeanPostProcessor    : postProcessBeforeInitialization, beanName = prototypePerson
    2020-03-03 11:42:27.228  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : afterPropertiesSet
    2020-03-03 11:42:27.228  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : customInit
    2020-03-03 11:42:27.228  INFO 312788 --- [           main] c.j.s.l.processor.MyBeanPostProcessor    : postProcessAfterInitialization, beanName = prototypePerson
    2020-03-03 11:42:27.228  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : getter
    2020-03-03 11:42:27.228  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : constructor: Instantiate
    2020-03-03 11:42:27.228  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setter: populate property
    2020-03-03 11:42:27.228  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setBeanName: prototypePerson
    2020-03-03 11:42:27.228  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setBeanFactory
    2020-03-03 11:42:27.229  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : setApplicationContext
    2020-03-03 11:42:27.229  INFO 312788 --- [           main] c.j.s.l.processor.MyBeanPostProcessor    : postProcessBeforeInitialization, beanName = prototypePerson
    2020-03-03 11:42:27.229  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : afterPropertiesSet
    2020-03-03 11:42:27.229  INFO 312788 --- [           main] com.jake.spring.lifecycle.domain.Person  : customInit
    2020-03-03 11:42:27.229  INFO 312788 --- [           main] c.j.s.l.processor.MyBeanPostProcessor    : postProcessAfterInitialization, beanName = prototypePerson
    

由单元测试的结果可知:

  • 运行 @SpringBootTest 注解的单元测试类和直接运行 Application 在容器启动部分是一致的,都是默认初始化单例 Bean。
  • @Lazy@Scope("prototype") 注解的 @Bean 配置,都是在被 ApplicationContext#getBean(String beanName) 调用时才进行初始化。
  • @Lazy 注解的 Bean,仅仅做一次初始化,获取的单例,仅仅是延迟初始化而已。
  • @Scope("prototype") 注解的 Bean,基于原型模式,其背后原理是拷贝,每次都进行初始化。

参考博客

  • Spring Bean 生命周期 (实例结合源码彻底讲透)
  • https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/ApplicationContextAware.html
  • https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/InitializingBean.html
  • https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/BeanPostProcessor.html
  • https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/DisposableBean.html
  • https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Bean.html
  • https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html
  • https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章