Spring中@PostConstruct的實(shí)現(xiàn)方法
前言
大多數(shù)java
程序員都使用過(guò)@PostConstruct
注解,它的作用就是在Bean
初始化完成后執(zhí)行,相當(dāng)于我們常說(shuō)的init()
方法。但是我們看@PostConstruct
只有單單的一個(gè)注解,它到底是如何實(shí)現(xiàn)在Bean
初始化完成后就被調(diào)用的呢?
源碼分析
我們通過(guò)idea
搜索發(fā)現(xiàn),只有CommonAnnotationBeanPostProcessor
這個(gè)類使用了@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(); ? ? ? } ? ? } }
通過(guò)源碼發(fā)現(xiàn),這顯然就是一個(gè)BeanPostProcessor
的子類啊,它在Spring
的生命周期中起作用,所以我們可以重點(diǎn)關(guān)注postProcessBeforeInitialization()
方法:
?public?Object?postProcessBeforeInitialization(Object?bean,?String?beanName)?throws?BeansException?{ ? ? ? ?// 獲取被@PostConstruct注解的方法元數(shù)據(jù) ? ? ? ?InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata?metadata?=?this.findLifecycleMetadata(bean.getClass()); ? ? ? ? ?try?{ ? ? ? ? ? ?// 調(diào)用目標(biāo)方法 ? ? ? ? ? ?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); ? ? ? } ? }
當(dāng)Bean
初始化完成后,postProcessBeforeInitialization()
方法將被調(diào)用,所有被注解了@PostConstruct
都會(huì)被調(diào)用,無(wú)論這個(gè)方法是在父類還是子類中:
? ?private?InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata?findLifecycleMetadata(Class<?>?clazz) { ? ? ? ?// 如果緩存為null,那么構(gòu)建緩存;這個(gè)緩存是存儲(chǔ)Bean中所有被@PostConstruct注解的方法元數(shù)據(jù) ? ? ? ?if?(this.lifecycleMetadataCache?==?null) { ? ? ? ? ? ?// 構(gòu)建緩存 ? ? ? ? ? ?return?this.buildLifecycleMetadata(clazz); ? ? ? }?else?{ ? ? ? ? ?// 如果緩存不為null,那么從緩存中取出所有被@PostConstruct注解的方法元數(shù)據(jù) ? ? ? ? ? ?InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata?metadata?=(InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata)this.lifecycleMetadataCache.get(clazz); ? ? ? ? ?// 如果緩存中取出來(lái)的元數(shù)據(jù)為null,這段代碼這種寫法是考慮到現(xiàn)在有多個(gè)線程,用了加鎖操作保證只有一個(gè)線程去構(gòu)建緩存buildLifecycleMetadata() ? ? ? ? ? ?if?(metadata?==?null) { ? ? ? ? ? ? ? ?synchronized(this.lifecycleMetadataCache) { ? ? ? ? ? ? ? ? ? ?metadata?=?(InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata)this.lifecycleMetadataCache.get(clazz); ? ? ? ? ? ? ? ? ?// 如果此時(shí)還沒(méi)拿到元數(shù)據(jù),就去構(gòu)建緩存 ? ? ? ? ? ? ? ? ? ?if?(metadata?==?null) { ? ? ? ? ? ? ? ? ? ? ? ?// 收集好元數(shù)據(jù) ? ? ? ? ? ? ? ? ? ? ? ?metadata?=?this.buildLifecycleMetadata(clazz); ? ? ? ? ? ? ? ? ? ? ? ?// 構(gòu)建緩存 ? ? ? ? ? ? ? ? ? ? ? ?this.lifecycleMetadataCache.put(clazz,?metadata); ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ?// 返回元數(shù)據(jù) ? ? ? ? ? ? ? ? ? ?return?metadata; ? ? ? ? ? ? ? } ? ? ? ? ? }?else?{ ? ? ? ? ? ? ? ?// 返回元數(shù)據(jù) ? ? ? ? ? ? ? ?return?metadata; ? ? ? ? ? } ? ? ? } ? }
1.緩存為null的情況下直接構(gòu)建緩存;
2.緩存不為null,就從緩存中取被注解的方法元數(shù)據(jù),沒(méi)取到就構(gòu)建緩存;
所以我們重點(diǎn)看看緩存是如何構(gòu)建的:
? ?private?InitDestroyAnnotationBeanPostProcessor.LifecycleMetadata?buildLifecycleMetadata(Class<?>?clazz) { ? ? ? ?// 如果這個(gè)類一定沒(méi)有被initAnnotationType或destroyAnnotationType注解 ? ? ? ?// 此時(shí)initAnnotationType就是我們的@PostConstruct注解 ? ? ? ?if?(!AnnotationUtils.isCandidateClass(clazz,?Arrays.asList(this.initAnnotationType,?this.destroyAnnotationType))) { ? ? ? ? ? ?return?this.emptyLifecycleMetadata; ? ? ? }?else?{ ? ? ? ? ? ?// 準(zhǔn)備好列表來(lái)裝被注解的方法 ? ? ? ? ? ?List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement>?initMethods?=?new?ArrayList(); ? ? ? ? ? ?List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement>?destroyMethods?=?new?ArrayList(); ? ? ? ? ? ?Class?targetClass?=?clazz; ? ? ? ? ? ?// 準(zhǔn)備循環(huán)向上遍歷所有的父類 ? ? ? ? ? ?do?{ ? ? ? ? ? ? ? ?List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement>?currInitMethods?=?new?ArrayList(); ? ? ? ? ? ? ? ?List<InitDestroyAnnotationBeanPostProcessor.LifecycleElement>?currDestroyMethods?=?new?ArrayList(); ? ? ? ? ? ? ? ?ReflectionUtils.doWithLocalMethods(targetClass, (method)?->?{ ? ? ? ? ? ? ? ? ? ?// 如果這個(gè)方法被@PostConstruct注解,那么就構(gòu)建元數(shù)據(jù)并放進(jìn)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); ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? }); ? ? ? ? ? ? ? ?// 先把當(dāng)前類被注解的方法元數(shù)據(jù)列表放進(jìn)initMethods頭部 ? ? ? ? ? ? ? ?initMethods.addAll(0,?currInitMethods); ? ? ? ? ? ? ? ?destroyMethods.addAll(currDestroyMethods); ? ? ? ? ? ? ? ?// 獲取當(dāng)前類的父類 ? ? ? ? ? ? ? ?targetClass?=?targetClass.getSuperclass(); ? ? ? ? ? ? ?// 準(zhǔn)備遍歷父類是否有被注解的方法,有的話收集好放進(jìn)initMethods頭部 ? ? ? ? ? }?while(targetClass?!=?null?&&?targetClass?!=?Object.class); ? ? ? ? ?// 返回構(gòu)建好的元數(shù)據(jù) ? ? ? ? ? ?return?initMethods.isEmpty()?&&?destroyMethods.isEmpty()???this.emptyLifecycleMetadata?:?newInitDestroyAnnotationBeanPostProcessor.LifecycleMetadata(clazz,?initMethods,?destroyMethods); ? ? ? } ? }
所以我們通過(guò)上述源碼的分析,最后得出以下結(jié)論:
1.
Bean
的父類方法也可以使用@PostConstruct
注解;2.執(zhí)行的時(shí)候是先執(zhí)行被
@PostConstruct
注解的父類方法,再執(zhí)行被@PostConstruct
注解的子類方法;3.被
@PostConstruct
注解的方法不能有任何參數(shù),可以通過(guò)new InitDestroyAnnotationBeanPostProcessor.LifecycleElement(method)
源碼驗(yàn)證;
以上就是Spring中@PostConstruct的實(shí)現(xiàn)方法的詳細(xì)內(nèi)容,更多關(guān)于Spring @PostConstruct實(shí)現(xiàn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java中 String和StringBuffer的區(qū)別實(shí)例詳解
這篇文章主要介紹了java中 String和StringBuffer的區(qū)別實(shí)例詳解的相關(guān)資料,一個(gè)小的例子,來(lái)測(cè)試String和StringBuffer在時(shí)間和空間使用上的差別,需要的朋友可以參考下2017-04-04Java使用線程實(shí)現(xiàn)異步運(yùn)行的方法
在Java中,實(shí)現(xiàn)異步運(yùn)行的一個(gè)常用方式是使用Thread類,這篇文章主要介紹了Java使用線程實(shí)現(xiàn)異步運(yùn)行,需要的朋友可以參考下2024-07-07SpringMVC JSON數(shù)據(jù)交互實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了SpringMVC JSON數(shù)據(jù)交互實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10Mybatis Plus 大數(shù)據(jù)游標(biāo)分頁(yè)的實(shí)現(xiàn)
使用MyBatis Plus的游標(biāo)分頁(yè),我們可以輕松應(yīng)對(duì)大數(shù)據(jù)量的場(chǎng)景,本文主要介紹了Mybatis Plus 大數(shù)據(jù)游標(biāo)分頁(yè)的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07Springboot通過(guò)Scheduled實(shí)現(xiàn)定時(shí)任務(wù)代碼
這篇文章主要介紹了Springboot通過(guò)Scheduled實(shí)現(xiàn)定時(shí)任務(wù)代碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11JavaSE實(shí)現(xiàn)電影院系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了JavaSE實(shí)現(xiàn)電影院系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08微服務(wù)架構(gòu)設(shè)計(jì)RocketMQ基礎(chǔ)及環(huán)境整合
這篇文章主要介紹了微服務(wù)架構(gòu)設(shè)計(jì)入門RocketMQ的基礎(chǔ)及環(huán)境整合實(shí)現(xiàn)步驟,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-10-10mybatis通過(guò)XML的方式拼接動(dòng)態(tài)sql
動(dòng)態(tài)SQL是一種在運(yùn)行時(shí)構(gòu)造和執(zhí)行SQL語(yǔ)句的技術(shù),這篇文章主要為大家介紹了mybatis如何通過(guò)XML的方式拼接動(dòng)態(tài)sql,有需要的小伙伴可以參考一下2024-12-12IO密集型任務(wù)設(shè)置線程池線程數(shù)實(shí)現(xiàn)方式
這篇文章主要介紹了IO密集型任務(wù)設(shè)置線程池線程數(shù)實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07