开启成长之旅!这是我参与「日新方案 12 月更文挑战」的第18天,点击检查活动详情

前语

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

源码剖析

咱们经过idea查找发现,只要CommonAnnotationBeanPostProcessor这个类使用了@PostConstruct:

publicclassCommonAnnotationBeanPostProcessorextendsInitDestroyAnnotationBeanPostProcessorimplementsInstantiationAwareBeanPostProcessor,BeanFactoryAware,Serializable{
  publicCommonAnnotationBeanPostProcessor() {
   this.setOrder(2147483644);
   // 给initAnnotationType赋值
   this.setInitAnnotationType(PostConstruct.class);
   this.setDestroyAnnotationType(PreDestroy.class);
   this.ignoreResourceType("javax.xml.ws.WebServiceContext");
   if(jndiPresent) {
     this.jndiFactory=newSimpleJndiBeanFactory();
   }
​
 }
}

经过源码发现,这明显就是一个BeanPostProcessor的子类啊,它在Spring的生命周期中起效果,所以咱们能够要点关注postProcessBeforeInitialization()办法:

publicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName)throwsBeansException{
   // 获取被@PostConstruct注解的办法元数据
   InitDestroyAnnotationBeanPostProcessor.LifecycleMetadatametadata=this.findLifecycleMetadata(bean.getClass());
​
   try{
     // 调用目标办法
     metadata.invokeInitMethods(bean,beanName);
     returnbean;
   }catch(InvocationTargetExceptionvar5) {
     thrownewBeanCreationException(beanName,"Invocation of init method failed",var5.getTargetException());
   }catch(Throwablevar6) {
     thrownewBeanCreationException(beanName,"Failed to invoke init method",var6);
   }
 }

Bean初始化完成后,postProcessBeforeInitialization()办法将被调用,一切被注解了@PostConstruct都会被调用,不管这个办法是在父类还是子类中:

 privateInitDestroyAnnotationBeanPostProcessor.LifecycleMetadatafindLifecycleMetadata(Class<?>clazz) {
   // 假如缓存为null,那么构建缓存;这个缓存是存储Bean中一切被@PostConstruct注解的办法元数据
   if(this.lifecycleMetadataCache==null) {
     // 构建缓存
     returnthis.buildLifecycleMetadata(clazz);
   }else{
    // 假如缓存不为null,那么从缓存中取出一切被@PostConstruct注解的办法元数据
     InitDestroyAnnotationBeanPostProcessor.LifecycleMetadatametadata=(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);
         }
         // 回来元数据
         returnmetadata;
       }
     }else{
       // 回来元数据
       returnmetadata;
     }
   }
 }

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

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

所以咱们要点看看缓存是如何构建的:

 privateInitDestroyAnnotationBeanPostProcessor.LifecycleMetadatabuildLifecycleMetadata(Class<?>clazz) {
   // 假如这个类必定没有被initAnnotationType或destroyAnnotationType注解
   // 此刻initAnnotationType就是咱们的@PostConstruct注解
   if(!AnnotationUtils.isCandidateClass(clazz,Arrays.asList(this.initAnnotationType,this.destroyAnnotationType))) {
     returnthis.emptyLifecycleMetadata;
   }else{
     // 预备好列表来装被注解的办法
     List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement>initMethods=newArrayList();
     List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement>destroyMethods=newArrayList();
     ClasstargetClass=clazz;
     // 预备循环向上遍历一切的父类
     do{
       List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement>currInitMethods=newArrayList();
       List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement>currDestroyMethods=newArrayList();
       ReflectionUtils.doWithLocalMethods(targetClass, (method)->{
         // 假如这个办法被@PostConstruct注解,那么就构建元数据并放进currInitMethods中
         if(this.initAnnotationType!=null&&method.isAnnotationPresent(this.initAnnotationType)) {
           InitDestroyAnnotationBeanPostProcessor.LifecycleElementelement=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(newInitDestroyAnnotationBeanPostProcessor.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);
    
    // 回来构建好的元数据
     returninitMethods.isEmpty()&&destroyMethods.isEmpty()?this.emptyLifecycleMetadata:newInitDestroyAnnotationBeanPostProcessor.LifecycleMetadata(clazz,initMethods,destroyMethods);
   }
 }

所以咱们经过上述源码的剖析,最终得出以下结论:

1.Bean的父类办法也能够使用@PostConstruct注解;

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

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