[Spring source code series] Bean life cycle

Keywords: Spring source code analysis bean

The life cycle of Bean in Spring

1. What is Bean?

All objects instantiated, assembled and managed by the Spring IoC container are called Bean objects. The Bean object itself can be regarded as a Pojo.

Explanation on the official website:

In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.

2. Bean life cycle

2.1. BeanDefinition reading & parsing

We can configure beans through XML or configuration annotations. The BeanDefinitionReader interface is responsible for this stage, which is implemented by its implementation class.

On the other hand, the Spring configuration file is read through the Resource interface definition:

Summary: file parsing process

  • Obtain the validation mode of XML file; (DTD and XSD mode, indicated in the document header)
  • Load the XML file and obtain the corresponding Doucument;
  • Register Bean information according to Document.

2.2, BeanDefinition Registration & loading

After obtaining the Document in the above way, we parse it into BeanDefinition, register it through the only real implementation subclass DefaultListableBeanFactory#registerBeanDefinition of BeanDefinitionRegistry, and store it in beanDefinitionMap.

At the same time, this process also involves the merging and transformation of BeanDefinition, such as the adoption of RootBeanDefinition and childbeandefinition with parent-child relationship

The DefaultListableBeanFactory#getMergedBeanDefinition method is merged into a final RootBeanDefinition. Because the parsed BeanDefinition exists in the form of GenericBeanDefinition, one-step conversion is also required.

What is father son relationship? That is, bean s can inherit, and subclasses enjoy the properties of the parent class.

Have you thought about why we need BeanDefinition? What we need is a Bean factory.

In fact, there is an Object beanClass in BeanDefinition; Use AbstractBeanFactory#resolveBeanClass to convert object type objects to class type for subsequent instantiation.

2.3 Bean instantiation

2.3.1. Before Bean instantiation

Before instantiation, you can implement beanfactoryprocessor#postprocessbeanfactory, and overwrite and add the read BeanDefinition properties according to the priority order. (note that the Bean object cannot be obtained in advance, which will lead to the early initialization of the Bean.)

This process is equivalent to an extension of BeanFactory.

2.3.2 Bean instantiation




At this stage, many operations involving instantiaawarebeanpostprocessor will be interspersed in the process of instance Bean. We mainly look at the calling mechanism of its implementation method.

First, use the AbstractAutowireCapableBeanFactory#createBean#doCreateBean#createBeanInstance method, and there will be different instance policies for different beans:

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// BeanDefinition load get class object
		Class<?> beanClass = resolveBeanClass(mbd, beanName);

		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}

		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}
		//1. If the factory method property is not empty, the factory method method is used to instantiate
		if (mbd.getFactoryMethodName() != null)  {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		// Shortcut when re-creating the same bean...
		boolean resolved = false;
		boolean autowireNecessary = false;
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		if (resolved) {
			if (autowireNecessary) {
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				return instantiateBean(beanName, mbd);
			}
		}

		//2. Determine the candidate constructor instantiation bean
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		//3. Otherwise, point to the default nonparametric Construction Policy:
    	   //Finally, the reflection is created by calling the ctor.newInstance method through BeanUtils#instantiateClass
		return instantiateBean(beanName, mbd);
	}

Of course, for dependent beans, the instantiation of dependent beans can be realized through the post enhancer. The processing principle is to implement the dependent beans first and then instance beans.

Here's the point. In the instantiation phase, you can perform enhancement operations. Specifically, in the abstractautowirecapablebeanfactory#createBean method, you can call the enhancement operation in resolvebeforeinstance. If the return is not empty, you can directly complete the instantiation of the Bean and exit; If the return is null, enter the createBean method of the upper layer to execute doCreateBean and continue to complete the normal execution process of spring.

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
			//Omit.....
try {
			// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}
		}
}

The resolvebeforeinstance here implements the enhanced operations before and after instantiation of applybeanpostprocessorsbeforeinstance and applyBeanPostProcessorsAfterInitialization respectively. It can be seen that if the enhancement is made before instantiation, the operations after instantiation and before initialization will be skipped and the Bean object will be created directly. In the next stage, we will fill in the attributes.

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			// Make sure bean class is actually resolved at this point.
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {
					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
					if (bean != null) {
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
			mbd.beforeInstantiationResolved = (bean != null);
		}
		return bean;
	}

As mentioned above, when our resolvebeforeinstance returns null, the object instance will be executed normally, and the doCreateBean method will trigger:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)	throws BeanCreationException {
	// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
            //If the bean instance is not created, create the bean first. Here, call createBeanInstance to return to the above
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
// Initialize the bean instance.
		Object exposedObject = bean;
		try {
            		//Attribute assignment
			populateBean(beanName, mbd, instanceWrapper);
            		//initializeBean goes in and triggers our initialization method
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}

Let's look at populateBean:

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
	// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
		// state of the bean before properties are set. This can be used, for example,
		// to support styles of field injection.
		boolean continueWithPropertyPopulation = true;

		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					//Instantiate the post processor (attribute assignment is also considered as the instantiation stage)
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
						continueWithPropertyPopulation = false;
						break;
					}
				}
			}
		}

Summary:

The pre initialization and post initialization methods defined by BeanPostProcessor will trigger the pre processor in resolvebeforeinstance; Or trigger pre and post processors in initializeBean.

The method defined by instantiawarebeanpostprocessor will trigger the pre processor by resolvebeforeinstance and the post processor by populateBean during instantiation.

2.3.3, attribute assignment

This step is to fill in the properties, specifically getPropertyValues in the instantiaawarebeanpostprocessor #populatebean. This method will eventually complete the property injection of tag property configuration in xml by calling the file.set method.

Two other implementations of this method:

AutowiredAnnotationBeanPostProcessor injects values into the fields and methods marked with @ Autowired and @ Value in this method.

CommonAnnotationBeanPostProcessor injects values into the fields and methods marked by @ Resource in this method.

2.4. Bean initialization

2.4.1 before Bean initialization

This procedure calls the AbstractAutowireCapableBeanFactory#initializeBea in the following order:

  • Preprocessor for applybeanpostprocessprocessorsbeforeinitialization #postprocessbeforeinitialization
  • Invokeinitialmethods # initialization method
  • Post processor for applyBeanPostProcessorsAfterInitialization#postProcessAfterInitialization

2.4.2 Bean initialization

Call InitializingBean#afterPropertiesSet method;

Then call the specified initialization method when defining bean.

Bean knows how to initialize methods:

Mode 1: specify the initialization method in xml mode

Method 2: @ Bean specifies the initialization method

@Bean(initMethod = "initialized method")

Mode 3: specify the initialization method by api

this.beanDefinition.setInitMethodName(methodName);

2.4.3 after Bean initialization

Call the postProcessAfterInitialization method of the BeanPostProcessor interface.

2.5. Bean usage

Call the getBean method directly.

2.6 Bean destruction

Poll the bean postprocessors list. If it is a destructionawarebeanepostprocessor, it will call its internal postProcessBeforeDestruction method;

If the bean implements the org.springframework.beans.factory.DisposableBean interface, the destroy method in this interface will be called;

Call the bean custom destruction method.

3. Interview

3.1´╝îFactoryBean

Sometimes instantiating a Bean is very complex. If it has more properties, it needs to configure a lot of information. To do this, you can customize the instantiation Bean logic by implementing the FactoryBean interface.

When we set the class attribute of the bean to the FactoryBean interface type in the configuration file, the bean obtained through the getBean method is not the FactoryBean itself, but the object returned by the FactoryBean#getObject() method. Here is a proxy. If you need to get the FactoryBean instance object, you need to explicitly getBean (& beanname).

3.2. L3 cache solves circular dependency

	/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name --> ObjectFactory */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name --> bean instance */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

Posted by ybinds on Fri, 17 Sep 2021 15:19:34 -0700