前言

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

正文

Springbean生命周期可以用下图进行概括。

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容器中的beansetApplicationContext()方法的调用时机在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


半夏之沫
65 声望31 粉丝