Spring Bean life cycle source code analysis

Posted by Niel Roos on Mon, 31 Jan 2022 05:37:21 +0100

Bean life cycle

preface

For ordinary Java objects, the object is created when it is new, and is recycled by the garbage collection mechanism when it has no reference. For objects managed by the Spring IoC container, their life cycle is completely controlled by the container. The life cycle of each Bean in Spring is as follows:

1, Instantiate Bean

For the BeanFactory container, when the customer requests a bean that has not been initialized from the container, or needs to inject another dependency that has not been initialized when initializing the bean, the container will call the doCreateBean method for instantiation. For the ApplicationContext container, all beans will be instantiated after the container is started. The container instantiates by obtaining the information in the BeanDefinition object. And this step is only a simple instantiation without dependency injection. The instantiated object is wrapped in the BeanWrapper object. BeanWrapper provides an interface to set object properties, thus avoiding the use of reflection mechanism to set properties

 protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {
        // Instantiate bean to BeanWrapper. Bean wrapper
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            // Instantiation: Reflection and factory;
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }

2, Set object properties (dependency injection)

The instantiated object is encapsulated in the BeanWrapper object, and the object is still in a native state without dependency injection. Next, Spring performs dependency injection according to the information in BeanDefinition.
And complete dependency injection through the interface for setting properties provided by BeanWrapper.

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {
        //Undertake the above
        // Initializing an instance of a Bean
        Object exposedObject = bean;
        try {
            // Populate bean s.
            populateBean(beanName, mbd, instanceWrapper); // Populate Bean properties
            // Initialize bean
            exposedObject = initializeBean(beanName, exposedObject, mbd); // Initialize the Bean and enter this method (3)
        }

3, initializeBean

initializeBean, you enter the next stage:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			// Call Aware method
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			// Post processor of calling bean
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
			// Call the initialized method
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
			// Call AfterInitialization of the bean's post processor. Place to implement AOP and dynamic agent
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean; // Return wrapper class
	}

3.1 calling Aware interface

Call Aware interface to inject desired attributes into Bean

private void invokeAwareMethods(String beanName, Object bean) {
    if (bean instanceof Aware) {
        // Aware of BeanName
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        // Aware of BeanClass
        if (bean instanceof BeanClassLoaderAware) {
            ClassLoader bcl = getBeanClassLoader();
            if (bcl != null) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
            }
        }
        // Aware of BeanFactory
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}

Spring will detect whether the object implements the xxxAware interface and inject the relevant xxxAware instance into the bean.
If the Bean implements the BeanNameAware interface, Spring passes the ID of the Bean to the setBeanName() method (the implementation of BeanNameAware is mainly to obtain the ID of the Bean through the reference of the Bean, which is rarely used in general business)
If the Bean implements the BeanFactoryAware interface, Spring will call the setbeandarty (BeanFactory BF) method and pass in the BeanFactory container instance as a parameter. (the main purpose of implementing BeanFactoryAware is to obtain the Spring container, such as Bean publishing events through the Spring container)
If the Bean implements the ApplicationContextAware interface, the Spring container will call the setApplicationContext(ApplicationContext ctx) method and pass the y application context as a parameter (the function is similar to that of BeanFactory in order to obtain the Spring container. The difference is that the Spring container will pass itself as the parameter of setApplicationContext when calling setApplicationContext method, and the Spring container needs the programmer to specify (inject) the parameter BeanFactory in setBeanDactory before calling setBeanDactory) 6 If the Bean implements the BeanPostProcess interface, Spring will call their postProcessBeforeInitialization method (to enhance the Bean instance after it is successfully created, such as modifying the Bean and adding a function)

3.2 pre processing of calling BeanPostProcessor

After the above steps, the bean object has been constructed correctly, but if you want to do some custom processing before the object is used, you can implement it through the BeanPostProcessor interface.
This interface provides two functions:
• postProcessBeforeInitialzation( Object bean, String beanName )
The bean object currently being initialized will be passed in, and we can do any processing on this bean.
This function will be executed before initializationbean, so it is called preprocessing.
The injection of all Aware interfaces is completed in this step.
• postProcessAfterInitialzation( Object bean, String beanName )
The bean object currently being initialized will be passed in, and we can do any processing on this bean.
This function will be executed after initializationbean is completed, so it is called post-processing.
What is called here is the pre-processing postprocessbeforeinitialization of Bean's post processor.

3.3 invokeInitMethods

protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		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 {
					// The method executed after setting the attribute is actually to set some states
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				((InitializingBean) bean).afterPropertiesSet();
			}
		}

		if (mbd != null && bean.getClass() != NullBean.class) {
			String initMethodName = mbd.getInitMethodName();
			if (StringUtils.hasLength(initMethodName) &&
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
				// Call custom initialization method
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}

InitializingBean and init method will enter this stage after the preprocessing of BeanPostProcessor is completed.

3.3.1 afterPropertiesSet()

The InitializingBean interface has only one function: at this stage, we can also add custom logic before the formal construction of the bean is completed, but it is different from pre-processing. Because this function does not pass in the current bean object, we can't deal with the object itself at this step, so we can only add some additional logic. To use it, we need to let the bean implement the interface and write the logic to be added in the function. Then, Spring will detect whether the current bean implements the interface after the preprocessing is completed, and execute the afterpropertieset function.

3.3.2 invokeCustomInitMethod

Of course, in order to reduce the intrusion to the client code, spring provides the bean configuration with the init method attribute, which specifies the name of the function to be executed at this stage. Spring will execute the functions we set in the initialization phase. Init method essentially still uses the InitializingBean interface.

3.4 post processing of calling BeanPostProcessor

When this step is reached, the Bean is ready and stays in the context of the application until it is destroyed.

4 destory

Like init method, by specifying a function for destroy method, the specified logic can be executed before the bean is destroyed.
Article reference from: https://www.zhihu.com/question/38597960/answer/247019950

Topics: Java Spring Interview