An overview,
1. Bean life cycle
The Bean life cycle includes the process of Bean creation, survival and destruction. Creation is the process of instantiation, survival includes attribute assignment and initialization, so the Bean life cycle is: instantiation, attribute assignment, initialization and destruction, and this process is different from singleton and multi-instance:
[1] Singleton beans
- Instantiation: Instantiates objects when the container is started
- Live: Bean objects live as long as the container exists (coexists with the container)
- Destroy: Destroy the container as soon as it is closed
[2] Multi-example beans
- Instantiate: Instantiate an object when it is acquired
- Live: Objects live as long as they are in use
- Destruction: Collected by the Java garbage collector when an object has not been used for a long time and no other object references it
2. Deal with the life cycle
In our development process, we generally do some processing before and after initialization, processing in many ways, can use annotations, can also use the implementation class, summarized as the following four points:
[1] Specify the method
You can specify initialization and destruction methods:
- Specified by the @bean annotation
initMethod
anddestroyMethod
[2] Implement the interface
Implementation interface:
-
InitializingBean interface: Defines the initialization logic
-
DisposableBean interface: Define the destruction logic
[3] JSR 250 annotation specification
JSR stands for Java Specification Requests, and the JSR 250 Specification contains annotations for injecting resources into endpoint implementation classes and annotations for managing the application lifecycle.
Using JSR:
-
PostConstruct annotation: Execute the initialization method after the Bean has been created and assigned
-
PreDestroy Note: Remind us to do a cleanup before the container destroys the container
[4] Post-processor
BeanPostProcessor interface: The Bean’s post-processor that does some processing before and after the Bean is initialized
-
PostProcessBeforeInitialization: before initialization work
-
PostProcessAfterInitialization: after initialization work
Ii. Case analysis
[1] Specify the method
You can specify initialization and destruction methods through the @bean annotation: initMethod and destroyMethod, using the Dog object as an example:
/ / start the class
public class MainTest {
@Test
public void TestMain(a) {
// Create an IOC container
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
// applicationContext.getBean("dog");applicationContext.close(); }}// The Bean to inject
public class Dog {
public Dog(a){
System.out.println("dog create");
}
public void init(a){
System.out.println("dog init");
}
public void destroy(a){
System.out.println("dog destroy"); }}/ / configuration class
@Configuration
public class AppConfig {
// @Scope("prototype")
// Specify the initialization and destruction methods via the @bean annotation
@Bean(initMethod = "init",destroyMethod = "destroy")
public Dog dog(a){
return newDog(); }}Copy the code
This is the case in the single-instance scenario, where the object is instantiated when the container is started and destroyed when the container is closed. As you can see, @bean (initMethod = “init”,destroyMethod = “destroy”) can specify the initialization and destruction methods.
If you have multiple instances, leave @Scope(“prototype”) out of the configuration class. Multiple instances instantiate objects when they are fetched. So you need to start classes in the applicationContext) getBean (” dog “); Release, get the object, run the startup class, create the Bean and initialize the Bean, and destroy is collected by the Java garbage collector when the object has not been used for a long time and no other object references it.
[2] Implement the interface
Implement the Bean object InitializingBean, DisposableBean interface, override destroy and afterPropertiesSet methods to destroy and initialize the Bean, note the @Component annotation to inject the Bean into the container, And use the @ComponentScan annotation in the configuration class to scan, using the CAT object as an example:
/ / start the class
@Test
public void TestMain(a) {
// Create an IOC container
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
applicationContext.close();
}
// The Bean to inject
@Component
public class Cat implements InitializingBean.DisposableBean {
public Cat(a){
System.out.println("cat create");
}
/ / destruction of Bean
public void destroy(a) throws Exception {
System.out.println("cat destroy");
}
// Initialize the Bean
public void afterPropertiesSet(a) throws Exception {
System.out.println("cat afterPropertiesSet"); }}/ / configuration class
@ComponentScan("bean")
@Configuration
public class AppConfig {}Copy the code
Implement the InitializingBean, DisposableBean interface, override the destroy and afterPropertiesSet methods to destroy and initialize the bean.
[3] Use JSR250 annotation specification
Use the JSR250 annotation specification to create, assign, and destroy objects using the @postConstruct and @Predestroy annotations. For example, pig:
/ / start the class
@Test
public void TestMain(a) {
// Create an IOC container
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
applicationContext.close();
}
// The Bean to inject
@Component
public class Pig {
public Pig(a){
System.out.println("pig create");
}
// @postconstruct: called after the object is created and assigned
@PostConstruct
public void init(a){
System.out.println("pig postConstruct");
}
// @predestroy: before the container removes the object
@PreDestroy
public void destroy(a){
System.out.println("pig destroy"); }}/ / configuration class
@ComponentScan("bean")
@Configuration
public class AppConfig {}Copy the code
The @postConstruct annotation is used to initialize the method and the @preDestroy annotation is used to initialize the method. Run the start class and you can see that initialization and destruction are performed:
[4] Post-processor
Post processor is do some work before and after the Bean is initialized, realize the BeanPostProcessor class, overriding methods, postProcessBeforeInitialization before initialization work, PostProcessAfterInitialization after initialization work, added in the pig object instance:
@Component // Add the post-processor to the container
public class MyBeanPostProcessor implements BeanPostProcessor {
/ / postProcessBeforeInitialization: before initialization work
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization" + beanName + "= = =" + bean);
return bean;
}
/ / postProcessAfterInitialization: after initialization work
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization" + beanName + "= = =" + bean);
returnbean; }}Copy the code
Execute the corresponding code before and after the pig object is initialized as follows:
Third, source tracking
BeanPostProcessor is a very important class in Spring source code. Let’s see how spring implements Bean initialization
[1] Refresh the container
The IOC container constructor calls the refresh method to refresh the container
public AnnotationConfigApplicationContext(String... basePackages) {
this(a);this.scan(basePackages);
// Refresh the container
this.refresh();
}
Copy the code
[2] Get the instance
Call the refresh method of finishBeanFactoryInitialization initialize all singleton
public void refresh(a) throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
// Initialize all singletons
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
this.destroyBeans();
this.cancelRefresh(var10);
throw var10;
} finally {
this.resetCommonCaches(); contextRefresh.end(); }}}Copy the code
- to
preInstantiateSingletons
In a class calledgetBean
methods - call
doGetBean
- call
getSingleton
Get a single instance - If not, call
createBean
Create an instance
this.getBean(beanName);
Copy the code
public Object getBean(String name) throws BeansException {
return this.doGetBean(name, (Class)null, (Object[])null.false);
}
Copy the code
if (mbd.isSingleton()) {
// Get a single instance
sharedInstance = this.getSingleton(beanName, () -> {
try {
// If not, call createBean to create the instance
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throwvar5; }}); bean =this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
Copy the code
// Create an instance
beanInstance = this.doCreateBean(beanName, mbdToUse, args);
Copy the code
[3] Create an instance
So how do we do that? Let’s go ahead and look at the source code, and trace back to the initializeBean method in the doCreateBean class, and there’s a populateBean method before that, right
- PopulateBean: Assigns a value to the Bean property
- InitializeBean: A call equivalent to a post-processor
try {
// Assign a value to the Bean property
this.populateBean(beanName, mbd, instanceWrapper);
// equivalent to a post-processor call
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) {
if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
throw (BeanCreationException)var18;
}
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
}
Copy the code
Call initializeBean method of invokeInitMethods method performs initialization method, in the realization of the above mentioned interface, using annotations, custom initialization procedures are carried out in this method, look at this method before and after:
- applyBeanPostProcessorsBeforeInitialization
- applyBeanPostProcessorsAfterInitialization
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if(System.getSecurityManager() ! =null) {
AccessController.doPrivileged(() -> {
this.invokeAwareMethods(beanName, bean);
return null;
}, this.getAccessControlContext());
} else {
this.invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null| |! mbd.isSynthetic()) { wrappedBean =this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
}
try {
// Execute the initialization method
this.invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable var6) {
throw newBeanCreationException(mbd ! =null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
}
if (mbd == null| |! mbd.isSynthetic()) { wrappedBean =this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
Copy the code
Click on these two methods to view the source code:
applyBeanPostProcessorsBeforeInitialization
- I’m going to go through all the BeanPostProcessors
- Perform BeforeInitialization one by one
- Null is returned directly, will not execute BeanPostProcessors. PostProcessBeforeInitialization
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
Object current;
/ / get all BeanPostProcessors traverse, each performing BeforeInitialization, null is returned directly, do not perform the back of the BeanPostProcessors. PostProcessBeforeInitialization
for(Iterator var4 = this.getBeanPostProcessors().iterator(); var4.hasNext(); result = current) {
BeanPostProcessor processor = (BeanPostProcessor)var4.next();
current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
returnresult; }}return result;
}
Copy the code
applyBeanPostProcessorsAfterInitialization
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
Object current;
for(Iterator var4 = this.getBeanPostProcessors().iterator(); var4.hasNext(); result = current) {
BeanPostProcessor processor = (BeanPostProcessor)var4.next();
current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
returnresult; }}return result;
}
Copy the code
Four,
BeanPostProcessors principle:
- through
getBean
For instance - If no, pass
createBean
Create an instance - in
doCreateBean
Class throughpopulateBean
Assign a value to an instance attribute - call
initializeBean
In the methodinvokeInitMethods
Method performs the initialization method - Executed before and after instance initialization
applyBeanPostProcessorsBeforeInitialization
,applyBeanPostProcessorsAfterInitialization
, which is what the post-processor does before and after the instance is initialized