Spring中@PostConstruct的实现方法

 更新时间:2023年06月30日 10:25:20   作者:湘小宝666  
大多数java程序员都使用过@PostConstruct注解,它的作用就是在Bean初始化完成后执行,相当于我们常说的init()方法,但是我们看@PostConstruct只有单单的一个注解,它到底是如何实现在Bean初始化完成后就被调用的呢,本文将详细给大家介绍一下

前言

大多数java程序员都使用过@PostConstruct注解,它的作用就是在Bean初始化完成后执行,相当于我们常说的init()方法。但是我们看@PostConstruct只有单单的一个注解,它到底是如何实现在Bean初始化完成后就被调用的呢?

源码分析

我们通过idea搜索发现,只有CommonAnnotationBeanPostProcessor这个类使用了@PostConstruct:

public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor implementsInstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
     public CommonAnnotationBeanPostProcessor() {
       this.setOrder(2147483644);
       // 给initAnnotationType赋值
       this.setInitAnnotationType(PostConstruct.class);
       this.setDestroyAnnotationType(PreDestroy.class);
       this.ignoreResourceType("javax.xml.ws.WebServiceContext");
       if (jndiPresent) {
           this.jndiFactory = new SimpleJndiBeanFactory();
      }
​
  }
}

通过源码发现,这显然就是一个BeanPostProcessor的子类啊,它在Spring的生命周期中起作用,所以我们可以重点关注postProcessBeforeInitialization()方法:

 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
       // 获取被@PostConstruct注解的方法元数据
       InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata metadata = this.findLifecycleMetadata(bean.getClass());
​
       try {
           // 调用目标方法
           metadata.invokeInitMethods(bean, beanName);
           return bean;
      } catch (InvocationTargetException var5) {
           throw new BeanCreationException(beanName, "Invocation of init method failed", var5.getTargetException());
      } catch (Throwable var6) {
           throw new BeanCreationException(beanName, "Failed to invoke init method", var6);
      }
  }

Bean初始化完成后,postProcessBeforeInitialization()方法将被调用,所有被注解了@PostConstruct都会被调用,无论这个方法是在父类还是子类中:

   private InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
       // 如果缓存为null,那么构建缓存;这个缓存是存储Bean中所有被@PostConstruct注解的方法元数据
       if (this.lifecycleMetadataCache == null) {
           // 构建缓存
           return this.buildLifecycleMetadata(clazz);
      } else {
         // 如果缓存不为null,那么从缓存中取出所有被@PostConstruct注解的方法元数据
           InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata metadata =(InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata)this.lifecycleMetadataCache.get(clazz);
         // 如果缓存中取出来的元数据为null,这段代码这种写法是考虑到现在有多个线程,用了加锁操作保证只有一个线程去构建缓存buildLifecycleMetadata()
           if (metadata == null) {
               synchronized(this.lifecycleMetadataCache) {
                   metadata = (InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata)this.lifecycleMetadataCache.get(clazz);
                 // 如果此时还没拿到元数据,就去构建缓存
                   if (metadata == null) {
                       // 收集好元数据
                       metadata = this.buildLifecycleMetadata(clazz);
                       // 构建缓存
                       this.lifecycleMetadataCache.put(clazz, metadata);
                  }
                   // 返回元数据
                   return metadata;
              }
          } else {
               // 返回元数据
               return metadata;
          }
      }
  }

1.缓存为null的情况下直接构建缓存;

2.缓存不为null,就从缓存中取被注解的方法元数据,没取到就构建缓存;

所以我们重点看看缓存是如何构建的:

   private InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata buildLifecycleMetadata(Class<?> clazz) {
       // 如果这个类一定没有被initAnnotationType或destroyAnnotationType注解
       // 此时initAnnotationType就是我们的@PostConstruct注解
       if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
           return this.emptyLifecycleMetadata;
      } else {
           // 准备好列表来装被注解的方法
           List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement> initMethods = new ArrayList();
           List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement> destroyMethods = new ArrayList();
           Class targetClass = clazz;
           // 准备循环向上遍历所有的父类
           do {
               List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement> currInitMethods = new ArrayList();
               List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement> currDestroyMethods = new ArrayList();
               ReflectionUtils.doWithLocalMethods(targetClass, (method) -> {
                   // 如果这个方法被@PostConstruct注解,那么就构建元数据并放进currInitMethods中
                   if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
                       InitDestroyAnnotationBeanPostProcessor.LifecycleElement element = newInitDestroyAnnotationBeanPostProcessor.LifecycleElement(method);
                       currInitMethods.add(element);
                       if (this.logger.isTraceEnabled()) {
                           this.logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
                      }
                  }
                   // 下面是判断是否被destroyAnnotationType注解
                   if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
                       currDestroyMethods.add(new InitDestroyAnnotationBeanPostProcessor.LifecycleElement(method));
                       if (this.logger.isTraceEnabled()) {
                           this.logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
                      }
                  }
​
              });
               // 先把当前类被注解的方法元数据列表放进initMethods头部
               initMethods.addAll(0, currInitMethods);
               destroyMethods.addAll(currDestroyMethods);
               // 获取当前类的父类
               targetClass = targetClass.getSuperclass();
             // 准备遍历父类是否有被注解的方法,有的话收集好放进initMethods头部
          } while(targetClass != null && targetClass != Object.class);
         // 返回构建好的元数据
           return initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata : newInitDestroyAnnotationBeanPostProcessor.LifecycleMetadata(clazz, initMethods, destroyMethods);
      }
  }

所以我们通过上述源码的分析,最后得出以下结论:

1.Bean的父类方法也可以使用@PostConstruct注解;

2.执行的时候是先执行被@PostConstruct注解的父类方法,再执行被@PostConstruct注解的子类方法;

3.被@PostConstruct注解的方法不能有任何参数,可以通过new InitDestroyAnnotationBeanPostProcessor.LifecycleElement(method)源码验证;

以上就是Spring中@PostConstruct的实现方法的详细内容,更多关于Spring @PostConstruct实现的资料请关注脚本之家其它相关文章!

相关文章

  • spring boot项目如何采用war在tomcat容器中运行

    spring boot项目如何采用war在tomcat容器中运行

    这篇文章主要介绍了spring boot项目如何采用war在tomcat容器中运行呢,主要讲述将SpringBoot打成war包并放入tomcat中运行的方法分享,需要的朋友可以参考下
    2022-11-11
  • Java中List.sort()自定义排序规则几种方式

    Java中List.sort()自定义排序规则几种方式

    Java中可通过Comparator匿名类、Lambda表达式、静态方法、自定义对象及实现Comparable接口实现List排序,这篇文章主要介绍了Java中List.sort()自定义排序规则几种方式,需要的朋友可以参考下
    2025-06-06
  • jfreechart画折线图的方法

    jfreechart画折线图的方法

    这篇文章主要为大家详细介绍了jfreechart画折线图的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • Java8与Scala中的Lambda表达式深入讲解

    Java8与Scala中的Lambda表达式深入讲解

    这篇文章主要给大家介绍了关于Java8与Scala中Lambda表达式的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11
  • 全面解析Java中常见Exception异常的错误排查与代码修正

    全面解析Java中常见Exception异常的错误排查与代码修正

    这篇文章主要为大家详细介绍了Java开发中最常见的异常类型及其处理方法,包括运行时异常(如NullPointerException,ArrayIndexOutOfBoundsException)和受检异常,下面小编就和大家详细介绍一下吧
    2026-03-03
  • java中XML的使用全过程

    java中XML的使用全过程

    这篇文章主要介绍了java中XML的使用全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-05-05
  • AsyncHttpClient的默认配置源码流程解读

    AsyncHttpClient的默认配置源码流程解读

    这篇文章主要为大家介绍了AsyncHttpClient的默认配置源码流程解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Java中随机函数变换的示例详解

    Java中随机函数变换的示例详解

    这篇文章主要为大家详细介绍了Java中随机函数的变换,文中的示例代码讲解详细,对我们学习Java有一定的帮助,感兴趣的可以了解一下
    2022-08-08
  • 深入理解Spring MVC的数据转换

    深入理解Spring MVC的数据转换

    这篇文章主要给大家介绍了关于Spring MVC数据转换的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起看看吧。
    2017-09-09
  • mybatis模糊查询like语句该如何写

    mybatis模糊查询like语句该如何写

    MyBatis模糊查询通常使用LIKE关键字,结合concat函数拼接通配符%实现,在MyBatis配置文件中,通过#{keyword}传递参数,生成带有通配符的查询语句,MyBatis-Plus中,通过LambdaQueryWrapper类和like方法构建模糊查询条件,简化查询操作
    2024-09-09

最新评论