[Spring Topic] Spring's Bean Life Cycle Source Code Analysis - Phase 4 (Bean Destruction) (Expand, just understand)

foreword

What we are talking about here is the destruction process of Bean. Perhaps, when many friends talk about the destruction of Beans, they may think of garbage collection. Although both are doing the last part of the life cycle, they are actually not the same thing.Garbage collection is a JVM-level thing, and the Bean destruction mentioned here is a Spring thing, so of course it's not the same thing.

reading advice

The content of this lesson will be explained with the following code as the entry point:

		// 注册Bean的销毁接口
		try {
    
    
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
    
    
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;

This code is actually in the method of Spring instantiation AbstractAutowireCapableBeanFactory#doCreateBean(). Moreover, everyone knows the name of this method. This step is just to register the destruction logic, not the real destruction. Only when certain conditions are met will it be destroyed.
registerDisposableBeanIfNecessaryThe specific code is as follows:

  /**
     * 将给定bean添加到此工厂中的一次性bean列表中,注册其DisposableBean接口和/或给定的destroy方法,以便在工厂关闭时调用(如果适用)。只适用于单例。 
     * 参数: 
     * beanName—bean的名称—bean实例mbd—bean的bean定义 
     * 参见: 
     * RootBeanDefinition。isSingleton RootBeanDefinition。getDependsOn, registerDisposableBean, registerDependentBean
     */
    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.
                registerDisposableBean(beanName, new DisposableBeanAdapter(
                        bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, 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, getBeanPostProcessorCache().destructionAware, acc));
            }
        }
    }

Course content

1. When will the Bean be destroyed?

Bean destruction occurs during the shutdown of the Spring container. At this time, all singleton beans in Spring will be destroyed, and the destruction methods of the beans that implement the custom destruction logic will be executed. What we are going to introduce in this article is: how to implement custom Bean destruction logic .
When the Spring container is closed, it can be displayed closed, such as:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = (UserService) context.getBean("userService");
userService.test();

// 容器关闭
context.close();

Alternatively, register a shutdown hook:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

// 注册关闭钩子
context.registerShutdownHook();

Object newUser = context.getBean("user");
System.out.println(newUser);

[Note: Forcibly killing the process (kill pid) will not call the custom Bean destruction logic]

The process of Spring closing the container:

  1. First publish the ContextClosedEvent event
  2. Call the onCloese() method of lifecycleProcessor
  3. Destroy the singleton bean
    • Iterating over disposableBeans
      • Remove each disposableBean from the singleton pool
      • Call the destroy() of the disposableBean
      • If this disposableBean is also dependent on other beans, then other beans must also be destroyed
      • If this disposableBean also contains inner beans, remove these beans from the singleton pool
    • Clear manualSingletonNames, which is a Set, which stores the beanName of the singleton bean manually registered by the user
    • Clear allBeanNamesByType, it is a Map, the key is the bean type, and the value is an array of all beanNames of this type
    • Empty singletonBeanNamesByType, similar to allBeanNamesByType, except that only singleton Bean is saved

2. Implement custom Bean destruction logic

The implementation methods are as follows:

2.1 Implement DisposableBean or AutoCloseable interface

Bean code example that requires custom destruction: (implemented from: DisposableBean)

@Component
public class TestDestroyBean implements DisposableBean {
    
    
    public void test() {
    
    
        System.out.println("测试一下销毁方法");
    }

    @Override
    public void destroy() throws Exception {
    
    
        System.out.println("TestDestroyBean------自定义的Bean销毁方法");
    }
}

Or: (implemented from: AutoCloseable)

@Component
public class TestDestroyBean implements AutoCloseable {
    
    
    public void test() {
    
    
        System.out.println("测试一下销毁方法");
    }
    
    @Override
    public void close() throws Exception {
    
    
        System.out.println("TestDestroyBean------自定义的Bean销毁方法");
    }
}

2.2 Using the @PreDestroy annotation

There are three implementation methods:

@Component
public class TestDestroyBean {
    
    
    public void test() {
    
    
        System.out.println("测试一下销毁方法");
    }

    @PreDestroy
    public void close() throws Exception {
    
    
        System.out.println("TestDestroyBean------自定义的Bean销毁方法");
    }
}

2.3 Other methods (manually specify the name of the destruction method)

Of course, there are other ways, such as:

<bean destroy-method='xxx'>

or:

@Bean(destroyMethod = "xxx")

Or, beanDefinitionspecify the destruction method directly in it:

@Component
public class MyBeanPostProcessor implements MergedBeanDefinitionPostProcessor {
    
    
    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    
    
        if (beanName.equals("user")) {
    
    
            beanDefinition.setInitMethodName("myInit");
            beanDefinition.setDestroyMethodName("xxxx");
        }
    }
}

The three methods mentioned above have a special place, because they are manually specified, so you can set a special value: (inferred).
If the name of the destruction method is set to this, and the bean is not implemented DisposableBean, then, during the destruction process, it will be checked whether there is a method closeor not under the bean shutdown. If yes, it will be automatically bound to 【User-defined Destruction Method】.

3. Detailed explanation of the process and method of registering and destroying Bean

This destruction process has involved 【3 core classes, 6 core methods】

3.1 AbstractBeanFactory#requiresDestruction: Do you need to destroy it?

Method call chain: From the entry: registerDisposableBeanIfNecessary() call in
Full path: org.springframework.beans.factory.support.AbstractBeanFactory#requiresDestruction
Method annotation: Add the given bean to the list of disposable beans in this factory and register its DisposableBean interface and/or the given destroy method to call when the factory is closed (if applicable). Applies to singletons only.

The source code is as follows:

protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
    
    
    return (
            bean.getClass() != NullBean.class && (
                    DisposableBeanAdapter.hasDestroyMethod(bean, mbd) 
                    || (hasDestructionAwareBeanPostProcessors() 
                            && DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware))
            )
    );
}

Interpretation of the method: The key source code inside is actually divided into two steps. as follows:

  1. Is there a specified destroy method.DisposableBeanAdapter.hasDestroyMethod(bean, mbd)
  2. Whether there is DestructionAwareBeanPostProcessora bean post-processor that can sense destruction ( hasDestructionAwareBeanPostProcessors). If there is, traverseDisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessorCache().destructionAware)

3.2 DisposableBeanAdapter.hasDestroyMethod: Whether there is a destruction method

Method call chain: called by requiresDestruction() in 3.1
Full path: org.springframework.beans.factory.support.DisposableBeanAdapter#hasDestroyMethod
Method annotation: Check whether the given bean has any destruction method to call.

The source code is as follows:

public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
    
    
	return (bean instanceof DisposableBean || inferDestroyMethodIfNecessary(bean, beanDefinition) != null);
}

The content inside is actually very simple, the steps are as follows:

  1. Whether the current Bean implements the DisposableBean interface
  2. If not, call inferDestroyMethodIfNecessarythe inferred destruction method (described later)

3.3 DisposableBeanAdapter#inferDestroyMethodIfNecessary: ​​Infer the destruction method

Method call chain: called by hasDestroyMethod() in 3.2
Full path: org.springframework.beans.factory.support.DisposableBeanAdapter#inferDestroyMethodIfNecessary
method comment:
If the current value of the "destroyMethodName" attribute of a given beanDefinition is AbstractBeanDefinition. Then try to infer a destroy method. Candidate methods are currently limited to public parameterless methods (whether locally declared or inherited) named 'close' or 'shutdown'. Updates the "destroyMethodName" of the given BeanDefinition to null if no such method is found, otherwise it is set to the name of the inferred method. This constant is used as the default value of the @Bean#destroyMethod attribute, and the value of this constant can also be used in XML or in attributes. Also handles the java.io.Closeable and AutoCloseable interfaces, and invokes the "close" method reflectively when the bean is implemented.

The source code is as follows:

	@Nullable
	private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
    
    
		String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
		if (destroyMethodName == null) {
    
    
			destroyMethodName = beanDefinition.getDestroyMethodName();
			boolean autoCloseable = (bean instanceof AutoCloseable);
			if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
					(destroyMethodName == null && autoCloseable)) {
    
    

				// 当销毁方法名字等于"(inferred)",且bean不是DisposableBean实现类
				destroyMethodName = null;
				if (!(bean instanceof DisposableBean)) {
    
    
					if (autoCloseable) {
    
    
						destroyMethodName = CLOSE_METHOD_NAME;
					}
					else {
    
    
						try {
    
    
							destroyMethodName = bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
						}
						catch (NoSuchMethodException ex) {
    
    
							try {
    
    
								destroyMethodName = bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
							}
							catch (NoSuchMethodException ex2) {
    
    
								// no candidate destroy method found
							}
						}
					}
				}
			}
			beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
		}
		return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
	}

Interpretation of the method: There is nothing to interpret, just repeat the post and comment on it. Try to infer a destroy method. Candidate methods are currently limited to public parameterless methods (whether locally declared or inherited) named 'close' or 'shutdown'. Updates the "destroyMethodName" of the given BeanDefinition to null if no such method is found, otherwise it is set to the name of the inferred method. This constant is used as the default value of @Bean#destroyMethod attribute, and the value of this constant can also be used in <bean destroy-method=""> or attribute in XML. Also handles the java.io.Closeable and AutoCloseable interfaces, and invokes the "close" method reflectively when the bean is implemented.

3.4 AbstractBeanFactory#hasDestructionAwareBeanPostProcessors: Whether there is a perceptual destruction Bean post processor

Method call chain: called by requiresDestruction() in 3.1
Full path: org.springframework.beans.factory.support.AbstractBeanFactory#hasDestructionAwareBeanPostProcessors
method comment: returns whether the factory holds a DestructionAwareBeanPostProcessor, which will be applied to the single when it is closed example bean.

The source code is as follows:

	protected boolean hasDestructionAwareBeanPostProcessors() {
    
    
		return !getBeanPostProcessorCache().destructionAware.isEmpty();
	}

3.5 DisposableBeanAdapter.hasApplicableProcessors: Whether there are destruction-aware Bean post-processors applied to the current Bean

Method call chain: called by requiresDestruction() in 3.1
Full path: org.springframework.beans.factory.support.DisposableBeanAdapter#hasApplicableProcessors
Method annotation: Check whether a given bean has a destruction-aware post-processor applied to it.

The source code is as follows:

	public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) {
    
    
		if (!CollectionUtils.isEmpty(postProcessors)) {
    
    
			for (DestructionAwareBeanPostProcessor processor : postProcessors) {
    
    
				if (processor.requiresDestruction(bean)) {
    
    
					return true;
				}
			}
		}
		return false;
	}

The classic BeanPostProcessor has dealt with it, so let’s not talk about it

3.6 DefaultSingletonBeanRegistry#registerDisposableBean: Register the bean that needs to be destroyed

Method call chain: Called from the entry: registerDisposableBeanIfNecessary()
Full path: org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDisposableBean
Method annotation: Add the given bean to the list of destroyed beans in this registry.

The source code is as follows:

private final Map<String, Object> disposableBeans = new LinkedHashMap<>();


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

Method interpretation: The so-called registration is actually adding the current bean and some information to a cache map. When you need to use it, just traverse the map directly

3.7 Summary of the process of registering and destroying beans

Overall, there are 2 steps:

  1. It is a singleton bean, and it is judged whether it needs to be destroyed. The judgment steps are as follows: (The details of different Spring versions are different, but the whole is consistent
    • Whether the current Bean implements DisposableBeanthe interface, if yes, return true directly; otherwise, proceed to the process of [inferring the destruction method]
    • inferred destruction method
      • Whether destroyMethod is specified in BeanDefinition, and destroyMethod==(inferred). If yes, find out whether there is closea method or shutdownmethod under the current bean, and if yes, directly return the name of the destruction method
      • Or whether the current Bean implements the AutoCloseable interface, if so, directly return the name of the destruction method
    • If there is no result in [Inferred Destruction Method], then call [Aware Destruction Bean Post Processor] DestructionAwareBeanPostProcessor.requiresDestruction(bean) to make a judgment
      • Directly in ApplicationListenerDetector, if the current bean is an ApplicationListener subclass, it needs to be destroyed
      • InitDestroyAnnotationBeanPostProcessor makes the method that has @PreDestroy annotations need to be destroyed
  2. If it needs to be destroyed, adapt it to a DisposableBeanAdapter object and store it in disposableBeans (a LinkedHashMap)

4. Register and destroy Bean logic flow chart

insert image description here

5. Concept review

One of the more important post-processors used here is that InitDestroyAnnotationBeanPostProcessorit is defined as follows:

    /**
     *
     通用场景
     术语库
     beanpostprocessor实现,调用带注释的init和destroy方法。允许一个注释替代Spring的org.springframework.beans.factory.InitializingBean和org.springframework.beans.factory.DisposableBean回调接口。
     这个后处理器检查的实际注释类型可以通过“initAnnotationType”和“destroyAnnotationType”属性来配置。可以使用任何自定义注释,因为没有必需的注释属性。
     Init和destroy注释可以应用于任何可见性的方法:public、package-protected、protected或private。可以注释多个这样的方法,但建议分别只注释一个init方法和destroy方法。
     Spring的
     org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
     支持JSR-250开箱即用的javax.annotation.PostConstruct和javax.annotation.PreDestroy注释,
     就像init annotation和destroy annotation一样。此外,它还支持javax.annotation.Resource注释,
     用于注释驱动的命名bean注入。
     自:
     2.5
     参见:
     setInitAnnotationType, setDestroyAnnotationType
     作者:
     Juergen hoel
     以上翻译结果来自有道神经网络翻译(YNMT)· 通用场景
     逐句对照
     */
     public class InitDestroyAnnotationBeanPostProcessor
		implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable {
    
    
}

summarize

  1. Learned the Bean destruction process
  2. Learned the logic of registering and destroying beans
  3. Learned how to customize Bean destruction logic

Guess you like

Origin blog.csdn.net/qq_32681589/article/details/132316780