likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 75

基于 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
@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.initAnnotationTypethis.destroyAnnotationType,这两个类型其实就是@PostConstruct@PreDestroy注解。

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

最后,将当前的类型信息clazz,与initMethodsdestroyMethods集合一起封装为一个 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;
}

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

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

至此,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方法前面已经介绍过了,只不过这次可以直接从缓存中获取到已经完成解析的元信息。

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

后处理器的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
评论
请登录