preface
All beans in Spring exist in the Spring container, which is responsible for initializing beans, assembling beans and managing the bean life cycle.
There are two types of containers in Spring. The first is BeanFactory (org.springframework.beans.factory.BeanFactory), which is the simplest container and provides basic dependency injection functions; The second is ApplicationContext (org.springframework.context.ApplicationContext), which is created based on BeanFactory and provides application framework level services.
This article will review the Spring container's life cycle management of bean s in combination with a Spring learning note during the reading period.
Spring version: 5.3.2
Springboot version: 2.4.1
text
The bean life cycle of Spring can be summarized in the following figure.
Spring will be responsible for the whole bean life cycle, from initialization to destruction. This section will demonstrate and explain the bean life cycle with examples. The whole article will not cover the spring source code and Springboot source code. To set up the demo project, you only need to create the Maven project and introduce the following dependencies.
<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>
I bean initialization
Bean creation is to call the bean constructor to create a bean. This section mainly reviews a basic knowledge, that is, the initialization sequence of class attributes and object attributes when creating an object.
Create TestParentService as shown below.
@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."); } }
Then create TestSonService and inherit TestParentService, as shown below.
@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."); } }
Create a TestSonService object in the test program and observe the log printing. The test code is as follows.
public class TestClient { public static void main(String[] args) { TestSonService testSonService = new TestSonService(); } }
The log print results are as follows.
The above log printing shows the initialization sequence of class attributes and object attributes when creating objects, as shown below.
It can be summarized as follows.
- When creating an object, the class will be loaded and initialized first;
- If the parent class of a class is not initialized, the parent class will be initialized first, and then the child class will be initialized;
- When initializing a class, first assign an initial value to the static variable, and then execute the static code block of the class;
- After class initialization, the parent class object will be initialized first, and then the child class object will be initialized;
- When initializing an object, first assign an initial value to the object variable, then execute the common code block of the class, and finally execute the construction method.
II BeanNameAware
The bean can obtain the name of the bean in the container by implementing the BeanNameAware interface. Define a TestService and implement the BeanNameAware interface, as shown below.
@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); } }
Register the TestService object as a bean in the container based on JavaConfig. The configuration class BeanConfig is as follows.
@Configuration public class BeanConfig { @Bean public TestService testService() { return new TestService(); } }
The test code is shown below.
public class TestClient { public static void main(String[] args) { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class); } }
When the Spring container is created, the TestService object will be loaded into the container and its life cycle will be managed. The log printing is as follows.
After the bean implements the BeanNameAware interface, the bean name in the container can be obtained through the setBeanName() method, and the call time of the setBeanName() method is after the bean is created.
III BeanFactoryAware
Beans that implement the BeanFactoryAware interface can obtain references to BeanFactory. Modify the TestService to implement the BeanFactoryAware interface, as shown below.
@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")); } }
Run the test code and print the log as shown below.
Beans that implement the BeanFactoryAware interface can obtain BeanFactory through the setBeanFactory() method, so as to realize functions such as judging the scope of beans. The call time of the setBeanFactory() method is after the setBeanName() method.
IV ApplicationContextAware
Beans that implement the ApplicationContextAware interface can obtain references to ApplicationContext. Usually, you can obtain bean s managed by the Spring container through ApplicationContext. Modify the TestService to implement the ApplicationContextAware interface, as shown below.
@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."); } }
Run the test code and print the log as follows.
Beans that implement the ApplicationContextAware interface can obtain the ApplicationContext through the setApplicationContext() method, so as to obtain the beans in the Spring container through the ApplicationContext. The call time of the setApplicationContext() method is after the setBeanFactory() method.
V BeanPostProcessor
The interfaces analyzed in sections 2 to 4 can only perform some operations on beans that implement the interface. If you need to perform some operations on all beans in the Spring container, you need to provide an implementation class of the BeanPostProcessor interface. The BeanPostProcessor interface class diagram is shown below.
The BeanPostProcessor interface defines two default methods. The implementation class of the BeanPostProcessor interface can choose to rewrite them to implement specific business logic. Define the DecoratorService class and implement the BeanPostProcessor interface, as shown below.
@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; } }
Modify the BeanConfig and add the DecoratorService object to the Spring container, as shown below.
@Configuration public class BeanConfig { @Bean public TestService testService() { return new TestService(); } @Bean public DecoratorService decoratorService() { return new DecoratorService(); } }
Execute the test code and print the log as follows.
The postProcessBeforeInitialization() method overridden by the implementation class of the BeanPostProcessor interface will be executed after the setApplicationContext() method, and the postProcessAfterInitialization() method will be executed after the initialization method customized by the bean.
Vi InitializingBean
Beans that implement the InitializingBean interface can execute some initialization logic in the implemented afterPropertiesSet() method. Modify the TestService to implement the InitializingBean interface, as shown below.
@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."); } }
Run the test code and print the log as follows.
You can know that the execution time of the afterPropertiesSet() method is after the postProcessBeforeInitialization() method.
VII Custom initialization method of bean
You can customize the initialization method in the class and specify the custom initialization method of the bean when registering the object in the container. The TestService is modified as follows.
@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."); } }
Modify the BeanConfig as follows.
@Configuration public class BeanConfig { @Bean(initMethod = "initBean") public TestService testService() { return new TestService(); } @Bean public DecoratorService decoratorService() { return new DecoratorService(); } }
Execute the test code and print the log as shown below.
It can be seen that the execution time of the bean custom initialization method is after the afterPropertiesSet() method.
VIII DisposableBean
Beans that implement the DisposableBean interface can perform some operations before bean destruction. Modify the TestService and implement the DisposableBean interface, as shown below.
@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."); } }
Modify the test code as shown below.
public class TestClient { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanConfig.class); //Close the container and destroy the bean applicationContext.close(); } }
Run the test code and print the log as follows.
It can be seen that the call time of the destroy() method is after the postProcessAfterInitialization() method.
IX Custom destruction method of bean
You can customize the destruction method in the class and specify the custom destruction method of the bean when registering the object in the container. The TestService is modified as follows.
@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."); } }
Modify the BeanConfig as shown below.
@Configuration public class BeanConfig { @Bean(initMethod = "initBean", destroyMethod = "destroyBean") public TestService testService() { return new TestService(); } @Bean public DecoratorService decoratorService() { return new DecoratorService(); } }
Execute the test code and print the log as follows.
It can be seen that the execution time of the bean custom destruction method is after the destroy() method.
summary
The Spring container is responsible for the creation and destruction of beans inside the container. The bean that implements the BeanNameAware interface can obtain the name of the bean in the container; Beans that implement the BeanFactoryAware interface can obtain references to BeanFactory; The bean that implements the ApplicationContextAware interface can obtain the reference of ApplicationContext; Beans that implement the InitializingBean interface can do some operations after bean initialization; Beans that implement the DisposableBean interface can do something before the bean is destroyed. In addition, you can define the implementation class of the BeanPostProcessor interface, and its overridden methods will act on each bean in the container.