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

相关文章

最新评论