preface

This article focuses on how to manage the Spring Bean lifecycle in the Spring IoC container.

In application development, there are often specific initialization tasks that are relatively fixed, such as establishing a database connection, opening a network connection, etc. At the same time, there are relatively fixed destruction tasks that need to be performed at the end of the service. To facilitate the design of these tasks, the Spring IoC container provides the ability to apply initialization and destruction processes for custom beans.

Spring Bean life cycle

Picture description

Let’s start with a Spring Bean lifecycle flowchart. Look at the following description with the picture to make it easier.

Text description

  1. The Bean container finds the Spring Bean definition in the configuration file.
  2. The Bean container uses the Java Reflection API to create instances of beans.
  3. If any properties are declared, the declared properties are set. If the property itself is a Bean, it is parsed and set.
  4. If the Bean class implementsBeanNameAwareInterface, will be called by passing the Bean’s namesetBeanName()Methods.
  5. If the Bean class implementsBeanClassLoaderAwareInterface, is called by passing an instance of the ClassLoader object that loaded the BeansetBeanClassLoader()Methods.
  6. If the Bean class implementsBeanFactoryAwareInterface, is called by passing an instance of the BeanFactory objectsetBeanFactory()Methods.
  7. If any BeanPostProcessors objects associated with the BeanFactory have loaded beans, they will be called before the Bean properties are setpostProcessBeforeInitialization()Methods.
  8. If the Bean class is implementedInitializingBeanInterface, after setting all the Bean properties defined in the configuration fileafterPropertiesSet()Methods.
  9. If the Bean definition in the configuration file containsinit-methodProperty, the value of the property is resolved to the name of the method in the Bean class, and the method is invoked.
  10. Is called if any Bean post-handlers are attached to the Bean Factory objectpostProcessAfterInitialization()Methods.
  11. If the Bean class implementsDisposableBeanInterface, is called when the Application no longer needs the Bean referencedestroy()Methods.
  12. If the Bean definition in the configuration file containsdestroy-methodProperty, then the corresponding method definition in the Bean class will be invoked.

Examples demonstrate

Next, let’s use a simple DEMO to show you the flow of the entire life cycle, just to impress you.

  1. To define aPersonClass, implementedDisposableBean, InitializingBean, BeanFactoryAware, BeanNameAwareThese four interfaces, as well as custom onesinit-methodanddestroy-method. Here, if you are not familiar with these interfaces, you can take a look at the definitions of these interfaces.

public class Person implements DisposableBean.InitializingBean.BeanFactoryAware.BeanNameAware {

    private String name;

    Person() {
        System.out.println("Constructor of person bean is invoked!");
    }

    public String getName(a) {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("setBeanFactory method of person is invoked");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("setBeanName method of person is invoked");
    }

    public void init(a) {
        System.out.println("custom init method of person bean is invoked!");
    }

    //Bean initialization code equals to
    @Override
    public void afterPropertiesSet(a) throws Exception {
        System.out.println("afterPropertiesSet method of person bean is invoked!");
    }

    //Bean destruction code
    @Override
    public void destroy(a) throws Exception {
        System.out.println("DisposableBean Destroy method of person bean is invoked!");
    }

    public void destroyMethod(a) {
        System.out.println("custom Destroy method of person bean is invoked!"); }}Copy the code
  1. To define aMyBeanPostProcessorimplementationBeanPostProcessorInterface.
public class MyBeanPostProcessor implements BeanPostProcessor {


    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("post Process Before Initialization is invoked");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("post Process after Initialization is invoked");
        returnbean; }}Copy the code
  1. Configuration file, specifiedinit-methodanddestroy-methodattribute
<?xml version="1.0" encoding="UTF-8"? >
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="myBeanPostProcessor" class="ric.study.demo.ioc.life_cycle_demo_set.MyBeanPostProcessor" />
    <bean name="personBean" class="ric.study.demo.ioc.life_cycle_demo_set.Person"
          init-method="init" destroy-method="destroyMethod">
        <property name="name" value="Richard Yi" />
    </bean>

</beans>
Copy the code
  1. Start the container and destroy it
public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config-1.xml"); ((ClassPathXmlApplicationContext) context).destroy(); }}Copy the code
  1. The output
Constructor of person bean is invoked!
setBeanName method of person is invoked
setBeanFactory method of person is invoked
post Process Before Initialization is invoked
afterPropertiesSet method of person bean is invoked!
custom init method of person bean is invoked!
post Process after Initialization is invoked
DisposableBean Destroy method of person bean is invoked!
custom Destroy method of person bean is invoked!
Copy the code

You can see that the result is the same as what we described above.

The source code parsing

Let’s take a look at how the calls described above are implemented from a source code perspective.

In fact, if you read my previous article on Spring IoC dependency injection source code parsing, you should know the implementation of the above call.

This is the equivalent of taking the relevant parts out again.

Container initialization

In the Spring IoC dependency injection phase, there are three key steps to creating a Bean

  1. CreateBeanInstance instantiate ()
  2. populateBean(); Attribute assembly
  3. InitializeBean () handles various callback events after the Bean is initialized

InitializeBean () is responsible for handling the various callback events after the Bean is initialized.

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
		if(System.getSecurityManager() ! =null) {
			AccessController.doPrivileged(new PrivilegedAction<Object>() {
				@Override
				public Object run(a) {
					invokeAwareMethods(beanName, bean);
					return null;
				}
			}, getAccessControlContext());
		}
		else {
            // The code is self-explanatory
            BeanNameAware, BeanClassLoaderAware, or BeanFactoryAware
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		if (mbd == null| |! mbd.isSynthetic()) {/ / BeanPostProcessor postProcessBeforeInitialization callback
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
            // init-methods
            // Or the InitializingBean interface is implemented and the afterPropertiesSet() method is called
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw newBeanCreationException( (mbd ! =null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null| |! mbd.isSynthetic()) {/ / BeanPostProcessor postProcessAfterInitialization callback
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}
		return wrappedBean;
	}
Copy the code

InvokeAwareMethods first invokes a series of Aware interface implementations

private void invokeAwareMethods(final String beanName, final Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof BeanNameAware) {
				((BeanNameAware) bean).setBeanName(beanName);
			}
			if (bean instanceof BeanClassLoaderAware) {
				((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
			}
			if (bean instanceof BeanFactoryAware) {
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); }}}Copy the code

And then perform the BeanPostProcessor postProcessBeforeInitialization callback

	@Override
	public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
			result = beanProcessor.postProcessBeforeInitialization(result, beanName);
			if (result == null) {
				returnresult; }}return result;
	}
Copy the code

It then calls the initialization methods, which include the afterPropertiesSet method of InitializingBean and the specified init-method method,

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

		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null| |! mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isDebugEnabled()) {
				logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if(System.getSecurityManager() ! =null) {
				try {
					AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
						@Override
						public Object run(a) throws Exception {
							((InitializingBean) bean).afterPropertiesSet();
							return null;
						}
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throwpae.getException(); }}else{ ((InitializingBean) bean).afterPropertiesSet(); }}if(mbd ! =null) {
			String initMethodName = mbd.getInitMethodName();
			if(initMethodName ! =null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && ! mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); }}}Copy the code

And then perform the BeanPostProcessor postProcessAfterInitialization callback

	@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
			result = beanProcessor.postProcessAfterInitialization(result, beanName);
			if (result == null) {
				returnresult; }}return result;
	}
Copy the code

Ok, so far we’ve covered the various callback implementations during the Spring container initialization process Bean loading, and now the Spring container destruction phase.

Container closed

Similar to Bean initialization, you can see a call to the Bean destruction method when the container is closed. The destruction process goes like this. DestroyBeans ()-> destroyBeans() -> destroySingletons() -> destroySingleton() -> destroyBean() -> bean.destroy(), You’ll see that the Bean’s destruction method is eventually called.

protected void destroyBean(String beanName, DisposableBean bean) {
		/ / ignore

		// Actually destroy the bean now...
		if(bean ! =null) {
			try {
				bean.destroy();
			}
			catch (Throwable ex) {
				logger.error("Destroy method on bean with name '" + beanName + "' threw an exception", ex); }}/ / ignore
	}
Copy the code

Note: oh, here the type of the Bean is actually DisposableBeanAdapter, DisposableBeanAdapter is Spring Bean destroyed, management actually here using the adapter pattern. Take a look at the specific method destroy().

@Override
	public void destroy(a) {
		if(! CollectionUtils.isEmpty(this.beanPostProcessors)) {
			for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
				processor.postProcessBeforeDestruction(this.bean, this.beanName); }}if (this.invokeDisposableBean) {
			if (logger.isDebugEnabled()) {
				logger.debug("Invoking destroy() on bean with name '" + this.beanName + "'");
			}
			try {
				if(System.getSecurityManager() ! =null) {
					AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
						@Override
						public Object run(a) throws Exception {
							((DisposableBean) bean).destroy();
							return null;
						}
					}, acc);
				}
				else {
                    // Call the destroy() method to DisposableBean((DisposableBean) bean).destroy(); }}catch (Throwable ex) {
				String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
				if (logger.isDebugEnabled()) {
					logger.warn(msg, ex);
				}
				else {
					logger.warn(msg + ":"+ ex); }}}if (this.destroyMethod ! =null) {
            // Call the set destroyMethod
			invokeCustomDestroyMethod(this.destroyMethod);
		}
		else if (this.destroyMethodName ! =null) {
			Method methodToCall = determineDestroyMethod();
			if(methodToCall ! =null) { invokeCustomDestroyMethod(methodToCall); }}}Copy the code

When was the BeanPostProcessor registered with the container?

The previous article only described the callback implementation of the BeanPostProcessor class in the Spring Bean lifecycle, but did not indicate when the BeanPostProcessor was registered with the container. Now let’s introduce it.

When the Spring IoC container is initialized, it does some initialization, including the BeanPostProcessor register procedure. See my article IoC container initialization for more details.

Let’s just put the source code here.

Source location AbstractApplicationContext# refresh ()

@Override
	public void refresh(a) throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 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);

			try {
				// 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.
                / / here
				registerBeanPostProcessors(beanFactory);
			/ /... ignore}}Copy the code
	protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
	}
Copy the code

Source location PostProcessorRegistrationDelegate# registerBeanPostProcessors ()

public static void registerBeanPostProcessors( ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true.false);
// step1
		// Register BeanPostProcessorChecker that logs an info message when
		// a bean is created during BeanPostProcessor instantiation, i.e. when
		// a bean is not eligible for getting processed by all BeanPostProcessors.
		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

// step2
		// Separate between BeanPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
		List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>();
		List<String> orderedPostProcessorNames = new ArrayList<String>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
				priorityOrderedPostProcessors.add(pp);
				if (pp instanceofMergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); }}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else{ nonOrderedPostProcessorNames.add(ppName); }}// step3
		// First, register the BeanPostProcessors that implement PriorityOrdered.
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

		// Next, register the BeanPostProcessors that implement Ordered.
		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
		for (String ppName : orderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			orderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		sortPostProcessors(orderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, orderedPostProcessors);

		// Now, register all regular BeanPostProcessors.
		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
		for (String ppName : nonOrderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			nonOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

		// Finally, re-register all internal BeanPostProcessors.
		sortPostProcessors(internalPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, internalPostProcessors);

		// Re-register post-processor for detecting inner beans as ApplicationListeners,
		// moving it to the end of the processor chain (for picking up proxies etc).
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
	}
Copy the code

The above process can be divided into four steps:

  1. throughbeanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);The beanFactory () method gets a set of names that inherit from the BeanPostProcessor interface.
  2. After beans are divided into3, Ordered, nonOrderedThree categories, the first two categories are added to the sorting conditions of the post; (Spring can passPriorityOrderedandOrderedThe interface controls the priority of the processorMergedBeanDefinitionPostProcessorIt’s not the core point.
  3. The third step can be divided into the following small steps
    1. priorityOrderedPostProcessors, sort before register
    2. orderedPostProcessors, sort before register
    3. registerednonOrderedPostProcessors, is the general processor
    4. internalPostProcessors, sort before register
    5. Sign up for aApplicationListenerDetectorThe processor

When did DisposableBeanAdapter register with the container?

DisposableBeanAdapter has a different abstraction level from BeanPostProcessor, which is bound to the Bean, so it registers at the dependency injection stage of the Spring Bean. See my article on the source code for Spring IoC dependency injection.

Source location: AbstractAutowireCapableBeanFactory# doCreateBean ()

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
			throws BeanCreationException {
		// Omit the previous many steps, want to know can go to see the source code or my article

		// Register bean as disposable.
    	// This is the registration step to DisposableBeanAdapter
		try {
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}
Copy the code

Source location: AbstractBeanFactory# registerDisposableBeanIfNecessary ()

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) { AccessControlContext acc = (System.getSecurityManager() ! =null ? getAccessControlContext() : null);
		if(! mbd.isPrototype() && requiresDestruction(bean, mbd)) {if (mbd.isSingleton()) {
                // Register a DisposableBean implementation that will perform all destruction for a given bean.
                / / include: DestructionAwareBeanPostProcessors DisposableBean interfaces, custom destroy methods.
				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,
						newDisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc)); }}}Copy the code

conclusion

From container initialization to container destruction, as well as the timing of the callback event registration, I hope it will be helpful to you.

This article is published by OpenWrite!