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实现的资料请关注脚本之家其它相关文章!

相关文章

  • java中 String和StringBuffer的区别实例详解

    java中 String和StringBuffer的区别实例详解

    这篇文章主要介绍了java中 String和StringBuffer的区别实例详解的相关资料,一个小的例子,来测试String和StringBuffer在时间和空间使用上的差别,需要的朋友可以参考下
    2017-04-04
  • 详解Maven私服Nexus的安装与使用

    详解Maven私服Nexus的安装与使用

    这篇文章主要介绍了详解Maven私服Nexus的安装与使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • Java使用线程实现异步运行的方法

    Java使用线程实现异步运行的方法

    在Java中,实现异步运行的一个常用方式是使用Thread类,这篇文章主要介绍了Java使用线程实现异步运行,需要的朋友可以参考下
    2024-07-07
  • SpringMVC JSON数据交互实现过程解析

    SpringMVC JSON数据交互实现过程解析

    这篇文章主要介绍了SpringMVC JSON数据交互实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • Mybatis Plus 大数据游标分页的实现

    Mybatis Plus 大数据游标分页的实现

    使用MyBatis Plus的游标分页,我们可以轻松应对大数据量的场景,本文主要介绍了Mybatis Plus 大数据游标分页的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-07-07
  • Springboot通过Scheduled实现定时任务代码

    Springboot通过Scheduled实现定时任务代码

    这篇文章主要介绍了Springboot通过Scheduled实现定时任务代码,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • JavaSE实现电影院系统

    JavaSE实现电影院系统

    这篇文章主要为大家详细介绍了JavaSE实现电影院系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • 微服务架构设计RocketMQ基础及环境整合

    微服务架构设计RocketMQ基础及环境整合

    这篇文章主要介绍了微服务架构设计入门RocketMQ的基础及环境整合实现步骤,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2021-10-10
  • mybatis通过XML的方式拼接动态sql

    mybatis通过XML的方式拼接动态sql

    动态SQL是一种在运行时构造和执行SQL语句的技术,这篇文章主要为大家介绍了mybatis如何通过XML的方式拼接动态sql,有需要的小伙伴可以参考一下
    2024-12-12
  • IO密集型任务设置线程池线程数实现方式

    IO密集型任务设置线程池线程数实现方式

    这篇文章主要介绍了IO密集型任务设置线程池线程数实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07

最新评论