Spring 源码阅读 45:@PostConstruct 和 @PreDestroy 注解的处理

简介: 本文介绍了 InitDestroyAnnotationBeanPostProcessor 后处理器对`@PostConstruct`和`@PreDestroy`注解方法的解析和处理原理。

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 44:@Resource 注解的处理

概述

上一篇分析了 CommonAnnotationBeanPostProcessor 后处理器对标记了@Resource注解的属性和方法的解析以及注入处理。本篇来分析它对@PostConstruct@PreDestroy注解的处理,这两部分内容共同构成了 Spring 对 JSR-250 的支持。

对这两个注解的处理逻辑,大部分都是在 CommonAnnotationBeanPostProcessor 的父类 InitDestroyAnnotationBeanPostProcessor 中完成的,本文主要涉及到了以下三个后处理方法:

  • postProcessMergedBeanDefinition方法执行了对这两个注解所标记的方法的解析。
  • postProcessBeforeInitialization方法执行了对@PostConstruct注解的处理。
  • postProcessBeforeDestruction方法执行了对@PreDestroy注解的处理。

下面我们一一分析。

注解方法的解析

因为 Spring 注册到容器中的后处理器是 CommonAnnotationBeanPostProcessor,因此我们还是从它入手。首先看他的postProcessMergedBeanDefinition方法。

// org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition@OverridepublicvoidpostProcessMergedBeanDefinition(RootBeanDefinitionbeanDefinition, Class<?>beanType, StringbeanName) {
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
InjectionMetadatametadata=findResourceMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}

这个方法的后两行代码是用来解析@Resource注解相关内容的,上一篇中已经分析过了,我们只看第一行代码。这里调用了父类 InitDestroyAnnotationBeanPostProcessor 的同名方法,我们找到这个方法。

// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition@OverridepublicvoidpostProcessMergedBeanDefinition(RootBeanDefinitionbeanDefinition, Class<?>beanType, StringbeanName) {
LifecycleMetadatametadata=findLifecycleMetadata(beanType);
metadata.checkConfigMembers(beanDefinition);
}

这个方法在两个类中的实现很相似,不过用来处理的注解是不同的,我们还是按照代码执行的顺序来分析。

首先进入findLifecycleMetadata方法。

findLifecycleMetadata 方法

// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#findLifecycleMetadataprivateLifecycleMetadatafindLifecycleMetadata(Class<?>clazz) {
if (this.lifecycleMetadataCache==null) {
// Happens after deserialization, during destruction...returnbuildLifecycleMetadata(clazz);
   }
// Quick check on the concurrent map first, with minimal locking.LifecycleMetadatametadata=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);
         }
returnmetadata;
      }
   }
returnmetadata;
}

这个方法是为了获取生命周期相关的元信息(LifecycleMetadata),其实就是当前 Bean 类型中标记了@PostConstruct@PreDestroy注解的方法的元信息,首先会从lifecycleMetadataCache缓存中获取,如果缓存为空,或者缓存中获取不到当前类型对应的元信息,则通过buildLifecycleMetadata方法从类型信息中解析。

接下来,进入buildLifecycleMetadata方法。

buildLifecycleMetadata 方法

// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadataprivateLifecycleMetadatabuildLifecycleMetadata(finalClass<?>clazz) {
if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
returnthis.emptyLifecycleMetadata;
   }
List<LifecycleElement>initMethods=newArrayList<>();
List<LifecycleElement>destroyMethods=newArrayList<>();
Class<?>targetClass=clazz;
do {
finalList<LifecycleElement>currInitMethods=newArrayList<>();
finalList<LifecycleElement>currDestroyMethods=newArrayList<>();
ReflectionUtils.doWithLocalMethods(targetClass, method-> {
if (this.initAnnotationType!=null&&method.isAnnotationPresent(this.initAnnotationType)) {
LifecycleElementelement=newLifecycleElement(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(newLifecycleElement(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 :
newLifecycleMetadata(clazz, initMethods, destroyMethods));
}

首先,会对当前的类型和注解进行校验,确保它们是需要进行解析和处理的类型。这里要处理的注解类型是this.initAnnotationTypethis.destroyAnnotationType,这两个类型其实就是@PostConstruct@PreDestroy注解。

然后,在do-while循环中,会遍历clazz类型及其继承关系链上除了 Object 之外的所有类型,从这些类型的方法中,找到被标记了要处理的两个注解类型的方法,将他们的信息封装成对应的 LifecycleElement 对象,分别添加到initMethodsdestroyMethods集合中。

最后,将当前的类型信息clazz,与initMethodsdestroyMethods集合一起封装为一个 LifecycleMetadata 对象作为结果返回。

至此,这些元信息就解析完了,我们再看postProcessMergedBeanDefinition方法的第二行代码中的checkConfigMembers方法。

LifecycleMetadata 的 checkConfigMembers 方法。

// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata#checkConfigMemberspublicvoidcheckConfigMembers(RootBeanDefinitionbeanDefinition) {
Set<LifecycleElement>checkedInitMethods=newLinkedHashSet<>(this.initMethods.size());
for (LifecycleElementelement : this.initMethods) {
StringmethodIdentifier=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=newLinkedHashSet<>(this.destroyMethods.size());
for (LifecycleElementelement : this.destroyMethods) {
StringmethodIdentifier=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;
}

这个方法中,对元信息中的initMethodsdestroyMethods两个集合分别做了处理。分别遍历两个集合中所有的方法信息,将没有被checkConfigMembers方法处理过的所有方法进行标记并分别保存到checkedInitMethodscheckedDestroyMethods集合中。

这一步的目的,是为了避免同一个方法被多个同类型的后处理器执行多次。

至此,postProcessMergedBeanDefinition方法的执行逻辑就结束了,它主要是完成了被@PostConstruct@PreDestroy两个注解标记的方法元信息的解析。

接下来分别看看 Spring 是如何处理这些方法元信息的。

@PostConstruct 方法的处理

进入postProcessBeforeInitialization方法,这个方法的实现在 InitDestroyAnnotationBeanPostProcessor 类中。

// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization@OverridepublicObjectpostProcessBeforeInitialization(Objectbean, StringbeanName) throwsBeansException {
LifecycleMetadatametadata=findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
   }
catch (InvocationTargetExceptionex) {
thrownewBeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
   }
catch (Throwableex) {
thrownewBeanCreationException(beanName, "Failed to invoke init method", ex);
   }
returnbean;
}

这里的findLifecycleMetadata方法前面已经介绍过了,只不过这次可以直接从缓存中获取到已经完成解析的元信息。

剩下的部分,关键的代码只有一句,就是调用metadatainvokeInitMethods方法。

后处理器的postProcessBeforeInitialization方法是在 Bean 创建完成、执行初始化方法之前执行的,因此,这里也会执行 Bean 类型中定义的所有的被标记了@PostConstruct注解的方法。

我们进入invokeInitMethods方法。

// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata#invokeInitMethodspublicvoidinvokeInitMethods(Objecttarget, StringbeanName) throwsThrowable {
Collection<LifecycleElement>checkedInitMethods=this.checkedInitMethods;
Collection<LifecycleElement>initMethodsToIterate=         (checkedInitMethods!=null?checkedInitMethods : this.initMethods);
if (!initMethodsToIterate.isEmpty()) {
for (LifecycleElementelement : 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#invokepublicvoidinvoke(Objecttarget) throwsThrowable {
ReflectionUtils.makeAccessible(this.method);
this.method.invoke(target, (Object[]) null);
}

很简单,就是通过反射机制执行这个方法。

至此,所有的@PostConstruct方法就会在postProcessMergedBeanDefinition后处理器方法被调用时执行一次。

下面看@PreDestroy注解方法的处理。

@PreDestroy 方法的处理

进入postProcessBeforeDestruction方法。

// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeDestruction@OverridepublicvoidpostProcessBeforeDestruction(Objectbean, StringbeanName) throwsBeansException {
LifecycleMetadatametadata=findLifecycleMetadata(bean.getClass());
try {
metadata.invokeDestroyMethods(bean, beanName);
   }
catch (InvocationTargetExceptionex) {
Stringmsg="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 (Throwableex) {
logger.warn("Failed to invoke destroy method on bean with name '"+beanName+"'", ex);
   }
}

可以看到,跟上一部分分析的postProcessBeforeInitialization方法的执行逻辑是一样的,只不过这次处理的是元信息中checkedDestroyMethods集合中的元素。也就是在一个 Bean 实例被晓慧钱,postProcessBeforeDestruction方法被调用的时候,所有在 Bean 类型中定义的标记了@PreDestroy注解的方法都会执行一遍。

总结

本文介绍了 InitDestroyAnnotationBeanPostProcessor 后处理器对@PostConstruct@PreDestroy注解方法的解析和处理原理。

至此,CommonAnnotationBeanPostProcessor 后处理器相关的源码也全部分析完了。

目录
相关文章
|
3天前
|
Java 开发者 Spring
深入理解Spring Boot的@ComponentScan注解
【4月更文挑战第22天】在构建 Spring Boot 应用时,@ComponentScan 是一个不可或缺的工具,它使得组件发现变得自动化和高效。这篇博客将详细介绍 @ComponentScan 的基本概念、关键属性及其在实际开发中的应用。
20 4
|
5天前
|
Java 开发者 Spring
Spring Framework 中的 @Autowired 注解:概念与使用方法
【4月更文挑战第20天】在Spring Framework中,@Autowired 注解是实现依赖注入(Dependency Injection, DI)的一种非常强大的工具。通过使用 @Autowired,开发者可以减少代码中的引用绑定,提高模块间的解耦能力
28 6
|
1月前
|
XML Java 数据库连接
spring boot 参数的过滤注解与实战
在Spring Boot应用中,对于入参的过滤,通常会涉及到对Web层的数据验证和处理。Spring Boot借助Spring框架提供了强大的验证框架支持,主要基于JSR-303/JSR-380(Bean Validation API)规范,以及Spring自身的@Valid或@Validated注解来实现请求参数的验证。以下是一些常见的使用案例来展示如何对参数进行过滤和验证。
29 1
|
1月前
|
Java Spring 容器
【Java】Spring如何扫描自定义的注解?
【Java】Spring如何扫描自定义的注解?
35 0
|
1月前
|
Java 数据库 Spring
【spring(四)】Spring事务管理和@Transactional注解
【spring(四)】Spring事务管理和@Transactional注解
|
7天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
12天前
|
XML Java 数据格式
进阶注解探秘:深入Spring高级注解的精髓与实际运用
进阶注解探秘:深入Spring高级注解的精髓与实际运用
26 2
|
12天前
|
XML Java 数据格式
从入门到精通:Spring基础注解的全面解析
从入门到精通:Spring基础注解的全面解析
30 2
从入门到精通:Spring基础注解的全面解析
|
16天前
|
Java 容器
SpringBoot使用配置注解开启自动配置功能&整合spring-boot-configuration-processor
SpringBoot使用配置注解开启自动配置功能&整合spring-boot-configuration-processor
14 0
|
1月前
|
Java Spring
使用spring实现邮件的发送(含测试,源码,注释)
使用spring实现邮件的发送(含测试,源码,注释)
7 0