[destruction of beans] of [spring source code series]

Posted by pwicks on Tue, 08 Mar 2022 13:04:27 +0100


The 41st national professional ice sculpture competition with the theme of "Winter Olympic light, colorful ice lanterns" entered the second day in the ice city of Harbin. 60 ice sculpture experts launched a fierce competitive competition in the Harbin Ice Lantern Art Park.
Ice sculpture art

1. General

Bean destruction is the last step in the bean's life cycle. For example, when Tomcat and other containers are closed, the bean destruction method will be called, which will be analyzed step by step below.

2. Source code analysis

After the bean is created, a destroyed Adapter object will be registered for the bean,

	protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

		......

		if (instanceWrapper == null) {
			//Create object instance
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		Object bean = instanceWrapper.getWrappedInstance();
		Class<?> beanType = instanceWrapper.getWrappedClass();
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		......

		try {
			// Attribute assignment
			populateBean(beanName, mbd, instanceWrapper);
			// Initialize bean
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		......

		// Register bean as disposable.
		try {
			// Register destroyed bean s
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

The disposableBeans collection in the registerdisposablebeaninifnecessary method is responsible for collecting beans that need to be destroyed.

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 performs all destruction
				// work for the given bean: DestructionAwareBeanPostProcessors,
				// DisposableBean interface, custom destroy method.
				// Register the DisposableBeanAdapter object that destroys the bean
				registerDisposableBean(beanName,
						new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
			}
                ......

public void registerDisposableBean(String beanName, DisposableBean bean) {
		synchronized (this.disposableBeans) {
			this.disposableBeans.put(beanName, bean);
		}
	}

The DisposableBeanAdapter object is the class responsible for bean destruction. Does the collection bean implement the DisposableBean interface in this class

class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable 

Whether to configure the destroy method attribute and filter the interfaces of DestructionAwareBeanPostProcessor type, as shown in the following figure:

public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition,
			List<BeanPostProcessor> postProcessors, @Nullable AccessControlContext acc) {

		......

		this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
	}
private List<DestructionAwareBeanPostProcessor> filterPostProcessors(List<BeanPostProcessor> processors, Object bean) {
		List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
		if (!CollectionUtils.isEmpty(processors)) {
			filteredPostProcessors = new ArrayList<>(processors.size());
			for (BeanPostProcessor processor : processors) {
				if (processor instanceof DestructionAwareBeanPostProcessor) {
					DestructionAwareBeanPostProcessor dabpp = (DestructionAwareBeanPostProcessor) processor;
					if (dabpp.requiresDestruction(bean)) {
						filteredPostProcessors.add(dabpp);
					}
				}
			}
		}
		return filteredPostProcessors;

When will the bean be destroyed? When tomcat is closed, it will call the destruction method in the servlet, specifically through the class contextloaderlistener The contextDestroyed method in Java keeps looking down through the closeWebApplicationContext method. This is the use of servlet specification and is called all the way down.








Finally, it will enter the destroy method in the DisposableBeanAdapter class, which will be called according to the previous collection.

public void destroy() {
		// beanpostProcessor implementation class for processing @ PreDestroy annotation: InitDestroyAnnotationBeanPostProcessor
		if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
			for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
				processor.postProcessBeforeDestruction(this.bean, this.beanName);
			}
		}
		// Handle the destruction of beans that implement the DisposableBean interface
		if (this.invokeDisposableBean) {
			if (logger.isTraceEnabled()) {
				logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'");
			}
			try {
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((DisposableBean) this.bean).destroy();
						return null;
					}, this.acc);
				}
				else {
					((DisposableBean) this.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);
				}
			}
		}

		// Handle the destruction of beans in the configuration file and beans configured with destroy method
		if (this.destroyMethod != null) {
			invokeCustomDestroyMethod(this.destroyMethod);
		}
		else if (this.destroyMethodName != null) {
			Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
			if (methodToInvoke != null) {
				invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
			}
		}
	}

The order of bean destruction is as follows:
1) Judge whether the @ PreDestroy annotated beans need to be processed. If necessary, implement the class InitDestroyAnnotationBeanPostProcessor through beanpostProcessor;
2) Judge whether to handle the destruction of beans that implement the DisposableBean interface;
3) Judge whether to process the destruction of beans in the configuration file and beans configured with destroy method.

3. Case demonstration

Define beans and add three corresponding methods of destruction at the same time;

/**
 * @Author: wzj
 * @Date: 2021/7/2 11:32
 * @Desc:
 */
public class Wzj implements DisposableBean {
    public static Wzj factoryMethod() {
        CQ cq = new CQ();
        SC sc = new SC();
        return new Wzj(sc, cq);
    }


    @PreDestroy
    public void close() {
        System.out.println("adopt @PreDestroy: Instance destruction wzj");
    }

    public void destroyMethod() {
        System.out.println("Configuration through configuration file destroy-method: Destroy instance wzj");
    }

    @Override
    public void destroy() {
        System.out.println("adopt DisposableBean Interface: destroy instance wzj");
    }

The configuration file is as follows:

	<bean id="wzj"  class="com.wzj.bean.Wzj" factory-method="factoryMethod" destroy-method="destroyMethod"/>

Test class:

/**
 * @Author: wzj
 * @Date: 2021/3/30 15:08
 * @Desc: Test class
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring.xml"})
public class TestSpring {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void testDestroy() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Wzj wzj = (Wzj)applicationContext.getBean("wzj");
        applicationContext.getBeanFactory().destroyBean("wzj");
  
    }

result:

Some people may ask why beans can be destroyed many times. In fact, the destruction of beans is not the destruction of beans in the real sense, but the destruction method is executed before destruction, which may include logical operations such as closing database connections and closing network requests. Then the real destruction is performed by the Spring container, and its internal beans will disappear naturally, Bean destruction occurs when the Spring container is closed.

Topics: Spring