1, Foreword
Last time we were Teach you how to solve circular dependency step by step, and explore the mystery of L3 cache step by step The way Spring solves circular dependency is analyzed in. In Section 6, doCreateBean (AbstractAutowireCapableBeanFactory class) has vaguely seen several stages of the Bean's life cycle.
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { //Instantiate bean BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args); boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { //When added to the L3 cache, getEarlyBeanReference will return the singleton factory addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } Object exposedObject = bean; //Attribute assignment populateBean(beanName, mbd, instanceWrapper); //initialization exposedObject = initializeBean(beanName, exposedObject, mbd); if (earlySingletonExposure) { //Find from L2 cache Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { //Return the bean in the L2 cache, where it may be the object after proxy exposedObject = earlySingletonReference; } } //Prepare before destruction and register the beans to be destroyed registerDisposableBeanIfNecessary(beanName, bean, mbd); return exposedObject; }
doCreateBean basically describes the general framework of the Bean life cycle - instantiation, attribute assignment and initialization. Of course, the last step of the Bean is destruction.
Basic phase diagram
Please remember this picture. Maybe you won't know it later.
2, Instantiation and attribute assignment phase
When the container is started, it will scan the specified package according to the configuration or annotation, convert the classes in it into BeanDefinition, and concentrate them in the beanDefinitionMap variable of DefaultListableBeanFactory class.
Beandfinition in Spring can be used to describe the scope of a bean, whether it is lazy to load, whether it is a singleton, etc.
After the container is started, when we try to get a Bean from the container, the first step is to instantiate the Bean. To put it bluntly, you need to call the constructor.
Note that the resolveBeforeInstantiation method is invoked before calling doCreateBean.
1,resolveBeforeInstantiation
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { //Omit some codes Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } //Officially enter the life cycle Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance; } protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<?> targetType = determineTargetType(beanName, mbd); if (targetType != null) { //Find the instantiaawarebeanpostprocessor and execute the corresponding method bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); //When the return is not null, find the BeanPostProcessor and execute the corresponding method if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } mbd.beforeInstantiationResolved = (bean != null); } return bean; }
When resolvebeforeinstance returns a non null bean, createBean will return directly. In other words, the subsequent instantiation, attribute assignment and initialization phases will not be carried out. This step will give the BeanPostProcessor a chance to return the proxy instead of the current bean.
The core method of resolvebeforeinstance is as follows:
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) { //Get the implementation classes of all beanpostprocessors for (BeanPostProcessor bp : getBeanPostProcessors()) { //If it is of instantiaawarebeanpostprocessor type, execute the postprocessbeforeinstance method if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null) { return result; } } } return null; } public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; //Get the implementation classes of all beanpostprocessors for (BeanPostProcessor processor : getBeanPostProcessors()) { //Execute the postProcessAfterInitialization method one by one Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }
2,BeanPostProcessor
BeanPostProcessor is an extension interface provided to us by the container, also known as the post processor of Bean. We can implement this interface to add business specific logic.
The BeanPostProcessor implementation class is also an ordinary Bean. How does Spring ensure that the BeanPostProcessor implementation class is loaded before the Bean written by the developer?
This brings us back to the most familiar refresh method
public void refresh() { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); }
The registerBeanPostProcessors method will register the BeanPostProcessor and complete the loading of non lazy loaded singleton beans in finishBeanFactoryInitialization.
Thus, the BeanPostProcessor will be loaded before the business Bean, so it can be called before the business Bean is instantiated.
3,instantiateBean
Now you can enter doCreateBean. createBeanInstance contains the logic of instantiating beans and encapsulating them as beanwrappers, and internally provides a variety of instantiation methods.
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { //Convert bean to class object Class<?> beanClass = resolveBeanClass(mbd, beanName); //Based on Supplier instantiation Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } //Factory based method instantiation if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } //Omit some codes //Instantiation based on parameterized constructor Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } //Instantiation based on parameterless constructor return instantiateBean(beanName, mbd); }
Interested students can go deep into the interior of these methods, which will not be discussed here.
4,populateBean
After instantiation, it will enter the attribute assignment phase.
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { boolean continueWithPropertyPopulation = true; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { //Postprocessafterinstance is called if it is of InstantiationAwareBeanPostProcessor type if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation = false; break; } } } } //Once the postprocessafterinstance of any instantiaawarebeanpostprocessor returns false, the property injection phase will no longer be carried out and directly enter the next phase if (!continueWithPropertyPopulation) { return; } PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); //Inject by name if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } //Injection by type if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } //Omit the other two extension points of instantiaawarebeanpostprocessor and the following dependency checks if (pvs != null) { //Attribute injection applyPropertyValues(beanName, mbd, bw, pvs); } }
Here, the instantiation and attribute assignment phase ends. Let's sort out the extension points
3, Initialization phase
Start with initializeBean in doCreateBean
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { //1. Set Aware related dependencies if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } //2. Call the preprocessing of BeanPostProcessor Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } //3.1 calling the afterpropertieset method of InitializingBean //3.2 calling custom init method try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } //4. Call post-processing of BeanPostProcessor if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
1. Set Aware related dependencies
private void invokeAwareMethods(final String beanName, final Object bean) { if (bean instanceof Aware) { //If the bean implements the BeanNameAware interface, set the BeanName if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } //If the bean implements the BeanClassLoaderAware interface, set BeanClassLoader if (bean instanceof BeanClassLoaderAware) { ClassLoader bcl = getBeanClassLoader(); if (bcl != null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } } //If the bean implements the BeanFactory aware interface, set BeanFactory if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } } }
It can be seen from here that as long as the Bean implements the specified Aware interface, Spring will inject these Aware related information into the Bean.
2. Call preprocessing of BeanPostProcessor
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; //Get the implementation classes of all beanpostprocessors for (BeanPostProcessor processor : getBeanPostProcessors()) { //Call the postProcessBeforeInitialization method one by one Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }
This step will get the implementation classes of all BeanPostProcessor in the container and call the postProcessBeforeInitialization method one by one. If its return is not null, the latter result will overwrite the previous result.
Of course, when processing to ApplicationContextAwareProcessor, the following Aware dependencies will be set.
- EnvironmentAware
- EmbeddedValueResolverAware
- ResourceLoaderAware
- ApplicationEventPublisherAware
- MessageSourceAware
- ApplicationContextAware
When processing to InitDestroyAnnotationBeanPostProcessor, the method modified by @ PostConstruct annotation will be found and executed.
3,invokeInitMethods
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { //If the InitializingBean interface is implemented, the afterpropertieset method is called boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); } } //Call the method specified by init method declared in xml if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
4. Post processing of calling BeanPostProcessor
Similar to preprocessing, it only calls the postProcessAfterInitialization method, which will not be repeated.
Therefore, the initialization process can be subdivided into
4, Destruction phase
When the container is closed, it will enter the Bean destruction phase, and the code starts from the close() method of AbstractApplicationContext
Don't hurry to enter the close() method. First look at the last method in the first section of the code:
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) { AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null); //If the Bean is not multiple instances and needs to be destroyed when the container is closed if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) { if (mbd.isSingleton()) { //Bind a DisposableBeanAdapter to the current Bean registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc)); } else { // A bean with a custom scope... Scope scope = this.scopes.get(mbd.getScope()); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'"); } scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc)); } } } public void registerDisposableBean(String beanName, DisposableBean bean) { synchronized (this.disposableBeans) { this.disposableBeans.put(beanName, bean); } }
In registerdisposablebeaninifnecessary, bind the corresponding DisposableBeanAdapter object for each singleton Bean that needs to be destroyed when the container is closed. Finally, put these beans and their disposablebeanadapters into the map named disposableBeans for subsequent use.
Now let's enter the close() method of AbstractApplicationContext
You'll enter the destrysinglets method of DefaultSingletonBeanRegistry
public void destroySingletons() { String[] disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet()); for (int i = disposableBeanNames.length - 1; i >= 0; i--) { destroySingleton(disposableBeanNames[i]); } //Omit some codes }
First get the names of all the beans to be destroyed, call the destroySingleton method one by one, go all the way down, and finally enter the destroyBean of DefaultSingletonBeanRegistry
The core sentence
// Actually destroy the bean now... bean.destroy();
Obtain the DisposableBeanAdapter corresponding to the bean from disposableBeans according to beanName, and call its destroy method
public void destroy() { //Call the method decorated with the @ PreDestroy annotation //Specifically, you can follow up the InitDestroyAnnotationBeanPostProcessor class if (!CollectionUtils.isEmpty(this.beanPostProcessors)) { for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) { processor.postProcessBeforeDestruction(this.bean, this.beanName); } } //If the DisposableBean interface is implemented, the destroy method is called if (this.invokeDisposableBean) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((DisposableBean) this.bean).destroy(); return null; }, this.acc); } else { ((DisposableBean) this.bean).destroy(); } } //Call the custom destroy method method in xml if (this.destroyMethod != null) { invokeCustomDestroyMethod(this.destroyMethod); } else if (this.destroyMethodName != null) { Method methodToCall = determineDestroyMethod(this.destroyMethodName); if (methodToCall != null) { invokeCustomDestroyMethod(methodToCall); } } }
Here, the Bean destruction process is basically over. Let's use a figure to summarize:
5, Whole process of life cycle
We connect the whole stage
6, Code verification
Bean to be observed
public class A implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean { private int id; public int getId() { return id; } public void setId(int id) { this.id = id; System.out.println("Attribute assignment"); } private A() { System.out.println("instantiation "); } @PostConstruct public void postConstruct() { System.out.println("@PostConstruct Specified method"); } @Override public void setBeanClassLoader(ClassLoader classLoader) { System.out.println("BeanClassLoaderAware.setBeanClassLoader"); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("BeanFactoryAware.setBeanFactory"); } @Override public void setBeanName(String name) { System.out.println("BeanNameAware.setBeanName"); } @Override public void afterPropertiesSet() { System.out.println("InitializingBean.afterPropertiesSet"); } public void initMethod() { System.out.println("xml in init-method Specified method"); } @PreDestroy public void preDestroy() { System.out.println("@PreDestroy Specified method"); } @Override public void destroy() { System.out.println("DisposableBean.destroy"); } public void destroyMethod() { System.out.println("xml in destroy-method Specified method"); } }
Custom BeanPostProcessor
public class BeanPostProcessorImpl implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("a")) { System.out.println("BeanPostProcessor.postProcessBeforeInitialization"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (beanName.equals("a")) { System.out.println("BeanPostProcessor.postProcessAfterInitialization"); } return bean; } }
Custom InstantiationAwareBeanPostProcessorImpl
public class InstantiationAwareBeanPostProcessorImpl implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { if (beanName.equals("a")) { System.out.println("InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation"); } return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if (beanName.equals("a")) { System.out.println("InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation"); } //If false is returned here, all beans will not be assigned properties return true; } }
spring.xml configuration
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.yang.ym"/> <bean id="a" class="com.yang.ym.testBean.A" init-method="initMethod" destroy-method="destroyMethod"> <property name="id" value="1"/> </bean> <bean class="com.yang.ym.testBean.BeanPostProcessorImpl"/> <bean class="com.yang.ym.testBean.InstantiationAwareBeanPostProcessorImpl"/> </beans>
Test class
@Test public void get() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); A a = (A) context.getBean("a"); System.out.println("Close the container"); context.close(); }
Output results:
InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation instantiation InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation Attribute assignment BeanNameAware.setBeanName BeanClassLoaderAware.setBeanClassLoader BeanFactoryAware.setBeanFactory BeanPostProcessor.postProcessBeforeInitialization @PostConstruct Specified method InitializingBean.afterPropertiesSet xml in init-method Specified method BeanPostProcessor.postProcessAfterInitialization Close the container @PreDestroy Specified method DisposableBean.destroy xml in destroy-method Specified method
7, Summary
First remember the four major stages: instantiation, attribute assignment, initialization and destruction.
Within the life cycle, you can customize the Bean post processor and implement the interface for extension
- Container level extension points, instantiaawarebeanpostprocessor and BeanPostProcessor.
- Bean level extension points, Aware related interfaces, InitializingBean and DisposableBean.
In addition, you can specify extension points in the form of annotations or xml configuration
- @PostConstruct annotation
- @PreDestroy annotation
- Init method in xml
- Destroy method in xml
Then it will be much easier to remember the sequence of the life cycle:
getBean
- InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
- instantiation
- InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
- Attribute assignment
- Aware interface callback
- BeanPostProcessor.postProcessBeforeInitialization
- @Method specified by PostConstruct
- InitializingBean.afterPropertiesSet
- Method specified by init method in xml
- BeanPostProcessor.postProcessAfterInitialization
After closing the container
- @PreDestroy specified method
- DisposableBean.destroy
- Method specified by destroy method in xml