spring bean life cycle

Introduction

   In the process of using spring beans, a very important topic is the life cycle of beans. Since spring itself is also a dependency injection framework, it itself includes the creation and management of beans. Moreover, it also provides a lot of bean management interface. How to choose when we use the framework is also a topic worthy of discussion.

 

Bean life cycle

   Looking at the bean life cycle, it will be a bit confusing, because spring provides many options, such as implementing the interface InitializingBean, DisposableBean, adding annotation @PostConstruct, @PreDestroy and setting init-method, destroy-method in the bean configuration file Options can be added in the implementation to customize some user-defined behavior. What is the difference and relationship between them? When should we choose which implementation means? Before discussing these issues, let's take a look at its overall life cycle.

    In general, the life cycle of a bean is as follows:

     Obviously, as you can see from the above figure, the life cycle of a bean mainly goes through the process of bean initialization and dependency injection, checking spring awareness, and calling the lifecycle callback method. In the destruction phase of the bean, there is a process of calling the lifecycle callback method. In general, these stages are not complicated. We discuss each stage. To compare and verify the stages, we run through the stages with an example.

 

Bean Instantiation and DI

    The first stage is easy to understand, because we need to define the target objects and the objects they depend on in some way, so spring needs to scan these configuration files or java config code. In this way, the class information and related metadata information of the object to be constructed are obtained.

    After getting this information, we need to create a relationship diagram of a series of objects. At this time, it is necessary to call the constructors of these objects, and then call the property setting methods of some dependent objects to achieve the process of forming a complete object.

    Suppose we have the following class:

public class PersonBean {
	
	private static final Logger logger = LogManager.getLogger();
	
	private String name;
	
	public PersonBean() {
		logger.info("Constructor of person bean is called !!");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		logger.info("name property is manipulated !!");
		this.name = name;
	}
}

    This class is very simple, that is, it has a name attribute of type string. We added logging methods to the constructor and the method that sets the name property. When these methods are called, we can see the output log.

    Correspondingly, the content of our configuration file is as follows:

    

<?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 id="personBean" class="com.wrox.PersonBean">
        <property name="name" value="Dummy Person"/>
    </bean>
    
</beans>

 

   The method called is as follows:

public class App {
	private static final Logger logger = LogManager.getLogger();
	
    public static void main( String[] args ) {
    	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    	PersonBean bean = context.getBean(PersonBean.class);
	logger.info("bean name is : {}", bean.getName());
	context.close();
    }
}

   If we execute the program at this point, we will see the following output:

 

2018-02-23 20:55:02,997 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@51b279c9: startup date [Fri Feb 23 20:55:02 CST 2018]; root of context hierarchy
2018-02-23 20:55:03,044 [main] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [ApplicationContext.xml]
2018-02-23 20:55:03,275 [main] INFO  com.wrox.PersonBean - Constructor of person bean is called !!
2018-02-23 20:55:03,282 [main] INFO  com.wrox.PersonBean - name property is manipulated !!
2018-02-23 20:55:03,314 [main] INFO  com.wrox.App - bean name is : Dummy Person
2018-02-23 20:55:03,315 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@51b279c9: startup date [Fri Feb 23 20:55:02 CST 2018]; root of context hierarchy

   很显然,除了前面spring自带的log输出,主要的调用过程就是首先调用PeronBean的构造函数,然后调用设置属性name的方法。

 

check for spring awareness

    spring提供了一组仅用于spring内部使用的一组接口。一般来说,对于通用的依赖注入来说,我们希望尽量保证依赖注入功能的通用性和可移植性。对于spring awareness的这一组接口来说,它主要提供一些方法使得被创建的bean能够和创建bean的容器之间有一定的交互。比如BeanNameAware接口。我们可以通过实现它得到当前被构建的bean的名字。并根据这些信息实现一些特定的业务逻辑。

    在这里,我们的示例很简单,只需要添加一个对BeanNameAware接口的实现:

public class PersonBean implements BeanNameAware

    我们实现的这个方法细节如下:

    

@Override
public void setBeanName(String name) {
    logger.info("Bean with name {} get set", name);
}

    在xml配置文件不变的情况下,我们运行程序将得到如下的输出:

2018-02-23 21:04:40,636 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@51b279c9: startup date [Fri Feb 23 21:04:40 CST 2018]; root of context hierarchy
2018-02-23 21:04:40,680 [main] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [ApplicationContext.xml]
2018-02-23 21:04:40,908 [main] INFO  com.wrox.PersonBean - Constructor of person bean is called !!
2018-02-23 21:04:40,915 [main] INFO  com.wrox.PersonBean - name property is manipulated !!
2018-02-23 21:04:40,916 [main] INFO  com.wrox.PersonBean - Bean with name personBean get set
2018-02-23 21:04:40,946 [main] INFO  com.wrox.App - bean name is : Dummy Person
2018-02-23 21:04:40,947 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@51b279c9: startup date [Fri Feb 23 21:04:40 CST 2018]; root of context hierarchy

    可见,这个方法的调用过程是在对象创建好并设置好属性之后发生的。

    除了上述的这个接口,spring还有一系列的awareness接口,像BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware。实现这些接口的bean会在创建的过程中被调用。 

 

Bean Life-cycle callback

InitializingBean

   最早出现在spring里的一个生命周期中应用的回调方法大概就是InitializingBean接口方法了。如果在bean创建后我们需要一些自定义的构造行为,需要这个被构造的bean实现这个接口。并在接口的实现方法里进行具体的自定义行为。比如说,在示例里我们增加实现这个接口:

public class PersonBean implements BeanNameAware, InitializingBean 

   我们添加方法的实现如下:

 

@Override
public void afterPropertiesSet() throws Exception {
	logger.info("afterPropertiesSet method of person bean is called !!");
}

    运行程序将得到如下的输出:

2018-02-23 21:27:02,077 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@51b279c9: startup date [Fri Feb 23 21:27:02 CST 2018]; root of context hierarchy
2018-02-23 21:27:02,121 [main] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [ApplicationContext.xml]
2018-02-23 21:27:02,351 [main] INFO  com.wrox.PersonBean - Constructor of person bean is called !!
2018-02-23 21:27:02,359 [main] INFO  com.wrox.PersonBean - name property is manipulated !!
2018-02-23 21:27:02,360 [main] INFO  com.wrox.PersonBean - Bean with name personBean get set
2018-02-23 21:27:02,360 [main] INFO  com.wrox.PersonBean - afterPropertiesSet method of person bean is called !!
2018-02-23 21:27:02,390 [main] INFO  com.wrox.App - bean name is : Dummy Person
2018-02-23 21:27:02,391 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@51b279c9: startup date [Fri Feb 23 21:27:02 CST 2018]; root of context hierarchy

    很明显,这部分的方法将在我们构造好bean之后调用,但是在几个方法中比较靠后的地方了。

 

init-method

    还有一种方法,相对来说对spring本身的依赖更低一些。毕竟前一种方法的实现需要实现一个spring特定的接口。而这种方法并不需要,我们只需要在配置里指定这个初始化的方法。我们需要做两个修改,首先在配置文件里设定执行具体初始化的方法:

 

<bean id="personBean" class="com.wrox.PersonBean" init-method="init">
    <property name="name" value="Dummy Person"/>
</bean>

   然后我们在具体的PersonBean里实现init方法:

 

public void init() {
	logger.info("init bean for PersonBean");
}

 

   我们再执行程序,会发现有如下的输出结果:

2018-02-23 21:34:03,327 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@51b279c9: startup date [Fri Feb 23 21:34:03 CST 2018]; root of context hierarchy
2018-02-23 21:34:03,375 [main] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [ApplicationContext.xml]
2018-02-23 21:34:03,602 [main] INFO  com.wrox.PersonBean - Constructor of person bean is called !!
2018-02-23 21:34:03,612 [main] INFO  com.wrox.PersonBean - name property is manipulated !!
2018-02-23 21:34:03,614 [main] INFO  com.wrox.PersonBean - Bean with name personBean get set
2018-02-23 21:34:03,614 [main] INFO  com.wrox.PersonBean - afterPropertiesSet method of person bean is called !!
2018-02-23 21:34:03,615 [main] INFO  com.wrox.PersonBean - init bean for PersonBean
2018-02-23 21:34:03,649 [main] INFO  com.wrox.App - bean name is : Dummy Person
2018-02-23 21:34:03,649 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@51b279c9: startup date [Fri Feb 23 21:34:03 CST 2018]; root of context hierarchy

    和前面实现接口的比起来,它执行的顺序更加靠后一点。

 

@PostConstruct annotation

    和前面两种方法比起来,这种基于annotation的方法更加新一些。它是基于JSR250规范的一种实现。所有支持这种规范的容器都可以兼容它。这种实现的过程显得比较简洁。它需要在配置文件里添加对annotation的支持:

<context:annotation-config />

    然后我们定义一个用@PostConstruct修饰的方法:

 

@PostConstruct
public void postConstruct() {
	logger.info("postconstruct annotation");
}

    这时候程序的输出结果将如下:

 

2018-02-23 21:39:46,438 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@51b279c9: startup date [Fri Feb 23 21:39:46 CST 2018]; root of context hierarchy
2018-02-23 21:39:46,481 [main] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [ApplicationContext.xml]
2018-02-23 21:39:46,711 [main] INFO  com.wrox.PersonBean - Constructor of person bean is called !!
2018-02-23 21:39:46,720 [main] INFO  com.wrox.PersonBean - name property is manipulated !!
2018-02-23 21:39:46,721 [main] INFO  com.wrox.PersonBean - Bean with name personBean get set
2018-02-23 21:39:46,721 [main] INFO  com.wrox.PersonBean - postconstruct annotation
2018-02-23 21:39:46,721 [main] INFO  com.wrox.PersonBean - afterPropertiesSet method of person bean is called !!
2018-02-23 21:39:46,722 [main] INFO  com.wrox.PersonBean - init bean for PersonBean
2018-02-23 21:39:46,751 [main] INFO  com.wrox.App - bean name is : Dummy Person
2018-02-23 21:39:46,751 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@51b279c9: startup date [Fri Feb 23 21:39:46 CST 2018]; root of context hierarchy

   可见基于这种方式的执行方法会比前两种方式要优先级高一点。

 

@PostConstruct和BeanPostProcessor的辨析

    在前面的几种方法里,还有一个比较有意思的hook。就是BeanPostProcessor。这是一个比较特殊的注册处理接口。这个接口里有两个方法:

 

@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
	return bean;
}

@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	return bean;
}

  这两个方法是它的默认实现,这里并没有做任何的变动,而只是直接返回bean对象本身。我们在具体的应用里可以通过实现这个接口来添加自定义的行为。

   当然,这个接口有一个特殊的地方就是,我们所有添加的自定义行为将被应用到所有创建的bean上,而不仅仅是单独的一个bean里。

    我们来看一个具体的示例。在我们前面的示例里,我们添加如下的BeanPostProcessor实现:

 

public class CustomizedBeanPostProcessor implements BeanPostProcessor {
	private static final Logger logger = LogManager.getLogger();
	
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		logger.info("Post process before initialization for bean {} with name {}", bean, beanName);
		return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		logger.info("Post process after initialization for bean {} with name {}", bean, beanName);
		return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
	}
}

    为了让这个实现生效,我们需要在配置文件里声明这个实现:

 

<bean class="com.wrox.CustomizedBeanPostProcessor"/>

    这时候如果运行程序,我们将看到如下的输出:

 

2018-02-25 12:08:58,811 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@51b279c9: startup date [Sun Feb 25 12:08:58 CST 2018]; root of context hierarchy
2018-02-25 12:08:58,855 [main] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [ApplicationContext.xml]
2018-02-25 12:08:59,093 [main] INFO  com.wrox.CustomizedBeanPostProcessor - Post process before initialization for bean org.springframework.context.event.EventListenerMethodProcessor@23348b5d with name org.springframework.context.event.internalEventListenerProcessor
2018-02-25 12:08:59,093 [main] INFO  com.wrox.CustomizedBeanPostProcessor - Post process after initialization for bean org.springframework.context.event.EventListenerMethodProcessor@23348b5d with name org.springframework.context.event.internalEventListenerProcessor
2018-02-25 12:08:59,095 [main] INFO  com.wrox.CustomizedBeanPostProcessor - Post process before initialization for bean org.springframework.context.event.DefaultEventListenerFactory@37ceb1df with name org.springframework.context.event.internalEventListenerFactory
2018-02-25 12:08:59,095 [main] INFO  com.wrox.CustomizedBeanPostProcessor - Post process after initialization for bean org.springframework.context.event.DefaultEventListenerFactory@37ceb1df with name org.springframework.context.event.internalEventListenerFactory
2018-02-25 12:08:59,096 [main] INFO  com.wrox.PersonBean - Constructor of person bean is called !!
2018-02-25 12:08:59,107 [main] INFO  com.wrox.PersonBean - name property is manipulated !!
2018-02-25 12:08:59,108 [main] INFO  com.wrox.PersonBean - Bean with name personBean get set
2018-02-25 12:08:59,108 [main] INFO  com.wrox.CustomizedBeanPostProcessor - Post process before initialization for bean com.wrox.PersonBean@5fbe4146 with name personBean
2018-02-25 12:08:59,108 [main] INFO  com.wrox.PersonBean - postconstruct annotation
2018-02-25 12:08:59,108 [main] INFO  com.wrox.PersonBean - afterPropertiesSet method of person bean is called !!
2018-02-25 12:08:59,108 [main] INFO  com.wrox.PersonBean - init bean for PersonBean
2018-02-25 12:08:59,109 [main] INFO  com.wrox.CustomizedBeanPostProcessor - Post process after initialization for bean com.wrox.PersonBean@5fbe4146 with name personBean
2018-02-25 12:08:59,146 [main] INFO  com.wrox.App - bean name is : Dummy Person
2018-02-25 12:08:59,146 [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@51b279c9: startup date [Sun Feb 25 12:08:58 CST 2018]; root of context hierarchy

   我们看里面CustomizedBeanProcessor的日志输出,会发现它相关联的输出有几个,即有PersonBean相关的,也有两个额外的。这些也是spring内部创建的对象,只是我们的应用里不会直接使用。

    这里,还有一个让我们有点疑惑的地方,就是BeanPostProcessor接口和@PostConstruct的关系。BeanPostProcessor是一个spring框架提供给用户用来扩展特定集成实现的接口。它的方法被调用的时候,一般都是在对象已经被创建好之后,而且依赖已经注入到里面了。在一般的情况下我们会用的很少。而@PostConstruct相当于实现里面postProcessAfterInitialization方法的简便手段。

 

Bean destruction life-cycle

    spring在bean的生命周期里也对bean的解构提供了一些支持。比如@PreDestroy annotation。这个方法的实现也和前面@PostConstruct的实现差不多,就是在某个方法前添加这个annotation就可以了。

    比如我们在示例里添加代码如下:

 

@PreDestroy
public void preDestroy() {
	logger.info("predestroy annotation");
}

   如果运行代码,会发现有如下更多的日志输出:

 

2018-02-25 12:33:09,195 [main] INFO  com.wrox.PersonBean - predestroy annotation

 

    还有一个实现对象析构的接口,就是DisposableBean。我们可以尝试实现这个接口:

   

public class PersonBean implements BeanNameAware, InitializingBean, DisposableBean

    具体实现的方法如下:

    

@Override
public void destroy() throws Exception {
	logger.info("destroy method of person bean is called !!");
}

    再运行程序,将看到有如下额外的输出:

   

2018-02-25 12:39:28,519 [main] INFO  com.wrox.PersonBean - predestroy annotation
2018-02-25 12:39:28,519 [main] INFO  com.wrox.PersonBean - destroy method of person bean is called !!

    可以看到这里destroy方法的执行是在@PreDestroy标注的方法之后。

    还有一个我们可以利用的方法就是在配置文件里指定destroy-method。比如说我们将前面PersonBean的配置修改成如下:

<bean id="personBean" class="com.wrox.PersonBean" init-method="init" destroy-method="destroyBean">
    <property name="name" value="Dummy Person"/>
</bean>

    这个标注的destroy-method实现如下:

public void destroyBean() {
	logger.info("destroy bean for PersonBean");
}

    运行程序时,destroy bean对象的输出如下:

 

2018-02-25 13:29:07,298 [main] INFO  com.wrox.PersonBean - predestroy annotation
2018-02-25 13:29:07,298 [main] INFO  com.wrox.PersonBean - destroy method of person bean is called !!
2018-02-25 13:29:07,298 [main] INFO  com.wrox.PersonBean - destroy bean for PersonBean

    当然,如果我们要让这些destroy方法生效的话,我们需要在程序里显式的调用contxt.close方法。

 

各种选项的取舍

    总的来说,我们在bean创建的生命周期中,默认的基本步骤是调用对象的构造函数和设置属性的方法。在必要的时候,我们可以选择实现InitializeBean接口,定义init-method方法或者使用@PostConstruct标注。一般来说,我们没必要把每种方法都用到实际的项目里。在一些对项目工程的移植性有要求的地方,使用init-method配置或者@PostConstruct的方法会更加理想一些。而在一些对移植性没什么要求却希望对象能支持自定义行为的话,使用InitializeBean接口也是一个不错的选择。因为必须要实现一个接口,它使得我们不会忘记定义这个过程。

    在某些特殊的情况下,如果我们希望对所有创建的bean对象做一些自定义的操作或者定制的话,一种比较好的手段就是实现BeanPostProcessor接口。

 

参考材料

https://www.journaldev.com/2637/spring-bean-life-cycle

https://javabeat.net/life-cycle-management-of-a-spring-bean/

http://wideskills.com/spring/spring-bean-lifecycle

https://howtodoinjava.com/spring/spring-core/spring-bean-life-cycle/

https://stackoverflow.com/questions/29743320/how-exactly-works-the-spring-bean-post-processor

https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans

pro spring 5

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326059058&siteId=291194637