开启成长之旅!这是我参与「日新方案 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)
源码验证;