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 后处理器相关的源码也全部分析完了。

目录
相关文章
|
7天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
1天前
|
监控 Java 应用服务中间件
Spring Boot 源码面试知识点
【5月更文挑战第12天】Spring Boot 是一个强大且广泛使用的框架,旨在简化 Spring 应用程序的开发过程。深入了解 Spring Boot 的源码,有助于开发者更好地使用和定制这个框架。以下是一些关键的知识点:
13 6
|
2天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
12 3
|
3天前
|
JSON 前端开发 Java
【JAVA进阶篇教学】第七篇:Spring中常用注解
【JAVA进阶篇教学】第七篇:Spring中常用注解
|
5天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
13 1
|
5天前
|
JavaScript Java 开发者
Spring Boot中的@Lazy注解:概念及实战应用
【4月更文挑战第7天】在Spring Framework中,@Lazy注解是一个非常有用的特性,它允许开发者控制Spring容器的bean初始化时机。本文将详细介绍@Lazy注解的概念,并通过一个实际的例子展示如何在Spring Boot应用中使用它。
18 2
|
6天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
47 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
7天前
|
前端开发 Java
SpringBoot之自定义注解参数校验
SpringBoot之自定义注解参数校验
16 2
|
13天前
|
Java Spring
springboot自带的@Scheduled注解开启定时任务
springboot自带的@Scheduled注解开启定时任务
|
13天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。