Spring 源码阅读 45:@PostConstruct 和 @PreDestroy 注解的处理
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天,点击查看活动详情
基于 Spring Framework v5.2.6.RELEASE
概述
上一篇分析了 CommonAnnotationBeanPostProcessor 后处理器对标记了@``Resource
注解的属性和方法的解析以及注入处理。本篇来分析它对@PostConstruct
和@PreDestroy
注解的处理,这两部分内容共同构成了 Spring 对 JSR-250 的支持。
对这两个注解的处理逻辑,大部分都是在 CommonAnnotationBeanPostProcessor 的父类 InitDestroyAnnotationBeanPostProcessor 中完成的,本文主要涉及到了以下三个后处理方法:
postProcessMergedBeanDefinition
方法执行了对这两个注解所标记的方法的解析。postProcessBeforeInitialization
方法执行了对@PostConstruct
注解的处理。postProcessBeforeDestruction
方法执行了对@PreDestroy
注解的处理。
下面我们一一分析。
注解方法的解析
因为 Spring 注册到容器中的后处理器是 CommonAnnotationBeanPostProcessor,因此我们还是从它入手。首先看他的postProcessMergedBeanDefinition
方法。
// org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
这个方法的后两行代码是用来解析@Resource
注解相关内容的,上一篇中已经分析过了,我们只看第一行代码。这里调用了父类 InitDestroyAnnotationBeanPostProcessor 的同名方法,我们找到这个方法。
// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
LifecycleMetadata metadata = findLifecycleMetadata(beanType);
metadata.checkConfigMembers(beanDefinition);
}
这个方法在两个类中的实现很相似,不过用来处理的注解是不同的,我们还是按照代码执行的顺序来分析。
首先进入findLifecycleMetadata
方法。
findLifecycleMetadata 方法
// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#findLifecycleMetadata
private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
if (this.lifecycleMetadataCache == null) {
// Happens after deserialization, during destruction...
return buildLifecycleMetadata(clazz);
}
// Quick check on the concurrent map first, with minimal locking.
LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
synchronized (this.lifecycleMetadataCache) {
metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
metadata = buildLifecycleMetadata(clazz);
this.lifecycleMetadataCache.put(clazz, metadata);
}
return metadata;
}
}
return metadata;
}
这个方法是为了获取生命周期相关的元信息(LifecycleMetadata),其实就是当前 Bean 类型中标记了@PostConstruct
和@PreDestroy
注解的方法的元信息,首先会从lifecycleMetadataCache
缓存中获取,如果缓存为空,或者缓存中获取不到当前类型对应的元信息,则通过buildLifecycleMetadata
方法从类型信息中解析。
接下来,进入buildLifecycleMetadata
方法。
buildLifecycleMetadata 方法
// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
return this.emptyLifecycleMetadata;
}
List<LifecycleElement> initMethods = new ArrayList<>();
List<LifecycleElement> destroyMethods = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<LifecycleElement> currInitMethods = new ArrayList<>();
final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
LifecycleElement element = new LifecycleElement(method);
currInitMethods.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
currDestroyMethods.add(new LifecycleElement(method));
if (logger.isTraceEnabled()) {
logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
});
initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
new LifecycleMetadata(clazz, initMethods, destroyMethods));
}
首先,会对当前的类型和注解进行校验,确保它们是需要进行解析和处理的类型。这里要处理的注解类型是this.initAnnotationType
和this.destroyAnnotationType
,这两个类型其实就是@PostConstruct
和@PreDestroy
注解。
然后,在do-while
循环中,会遍历clazz类型及其继承关系链上除了 Object 之外的所有类型,从这些类型的方法中,找到被标记了要处理的两个注解类型的方法,将他们的信息封装成对应的 LifecycleElement 对象,分别添加到initMethods
和destroyMethods
集合中。
最后,将当前的类型信息clazz
,与initMethods
和destroyMethods
集合一起封装为一个 LifecycleMetadata 对象作为结果返回。
至此,这些元信息就解析完了,我们再看postProcessMergedBeanDefinition
方法的第二行代码中的checkConfigMembers
方法。
LifecycleMetadata 的 checkConfigMembers 方法。
// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata#checkConfigMembers
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
Set<LifecycleElement> checkedInitMethods = new LinkedHashSet<>(this.initMethods.size());
for (LifecycleElement element : this.initMethods) {
String methodIdentifier = element.getIdentifier();
if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) {
beanDefinition.registerExternallyManagedInitMethod(methodIdentifier);
checkedInitMethods.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Registered init method on class [" + this.targetClass.getName() + "]: " + element);
}
}
}
Set<LifecycleElement> checkedDestroyMethods = new LinkedHashSet<>(this.destroyMethods.size());
for (LifecycleElement element : this.destroyMethods) {
String methodIdentifier = element.getIdentifier();
if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) {
beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier);
checkedDestroyMethods.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Registered destroy method on class [" + this.targetClass.getName() + "]: " + element);
}
}
}
this.checkedInitMethods = checkedInitMethods;
this.checkedDestroyMethods = checkedDestroyMethods;
}
这个方法中,对元信息中的initMethods
和destroyMethods
两个集合分别做了处理。分别遍历两个集合中所有的方法信息,将没有被checkConfigMembers
方法处理过的所有方法进行标记并分别保存到checkedInitMethods
和checkedDestroyMethods
集合中。
这一步的目的,是为了避免同一个方法被多个同类型的后处理器执行多次。
至此,postProcessMergedBeanDefinition
方法的执行逻辑就结束了,它主要是完成了被@PostConstruct
和@PreDestroy
两个注解标记的方法元信息的解析。
接下来分别看看 Spring 是如何处理这些方法元信息的。
@PostConstruct 方法的处理
进入postProcessBeforeInitialization
方法,这个方法的实现在 InitDestroyAnnotationBeanPostProcessor 类中。
// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}
这里的findLifecycleMetadata
方法前面已经介绍过了,只不过这次可以直接从缓存中获取到已经完成解析的元信息。
剩下的部分,关键的代码只有一句,就是调用metadata
的invokeInitMethods
方法。
后处理器的postProcessBeforeInitialization
方法是在 Bean 创建完成、执行初始化方法之前执行的,因此,这里也会执行 Bean 类型中定义的所有的被标记了@PostConstruct
注解的方法。
我们进入invokeInitMethods
方法。
// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata#invokeInitMethods
public void invokeInitMethods(Object target, String beanName) throws Throwable {
Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods;
Collection<LifecycleElement> initMethodsToIterate =
(checkedInitMethods != null ? checkedInitMethods : this.initMethods);
if (!initMethodsToIterate.isEmpty()) {
for (LifecycleElement element : initMethodsToIterate) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking init method on bean '" + beanName + "': " + element.getMethod());
}
element.invoke(target);
}
}
}
首先获取到当前元信息的checkedInitMethods
集合,如果它为空则获取initMethods
集合,赋值给initMethodsToIterate
变量,然后对其中的元素进行遍历。
这里面的每一个元素其实都是一个之前解析到的@PostConstruct
方法的元信息element
,这里会分别执行其invoke
方法。
进入invoke
方法。
// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleElement#invoke
public void invoke(Object target) throws Throwable {
ReflectionUtils.makeAccessible(this.method);
this.method.invoke(target, (Object[]) null);
}
很简单,就是通过反射机制执行这个方法。
至此,所有的@PostConstruct
方法就会在postProcessMergedBeanDefinition
后处理器方法被调用时执行一次。
下面看@PreDestroy
注解方法的处理。
@PreDestroy 方法的处理
进入postProcessBeforeDestruction
方法。
// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeDestruction
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeDestroyMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
String msg = "Destroy method on bean with name '" + beanName + "' threw an exception";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex.getTargetException());
}
else {
logger.warn(msg + ": " + ex.getTargetException());
}
}
catch (Throwable ex) {
logger.warn("Failed to invoke destroy method on bean with name '" + beanName + "'", ex);
}
}
可以看到,跟上一部分分析的postProcessBeforeInitialization
方法的执行逻辑是一样的,只不过这次处理的是元信息中checkedDestroyMethods
集合中的元素。也就是在一个 Bean 实例被晓慧钱,postProcessBeforeDestruction
方法被调用的时候,所有在 Bean 类型中定义的标记了@PreDestroy
注解的方法都会执行一遍。
总结
本文介绍了 InitDestroyAnnotationBeanPostProcessor 后处理器对@PostConstruct
和@PreDestroy
注解方法的解析和处理原理。
至此,CommonAnnotationBeanPostProcessor 后处理器相关的源码也全部分析完了。
转载自:https://juejin.cn/post/7153799990371385374