作者:春卷要炸著吃
https://www.jianshu.com/p/dee681eb3e86
2020-02-12 11:12:00

在Spring專案經常遇到@PostConstruct注解,首先介紹一下它的用途: 被注解的方法,在物件加載完依賴注入后執行,
此注解是在Java EE5規范中加入的,在Servlet生命周期中有一定作用,它通常都是一些初始化的操作,但初始化可能依賴于注入的其他組件,所以要等依賴全部加載完再執行,
與之對應的還有@PreDestroy,在物件消亡之前執行,原理差不多,這里不做過多介紹,
那么首先看下原始碼注釋

PostConstruct介紹
總體概括如上,注意其中幾個點
1. 要在依賴加載后,物件使用前執行,而且只執行一次,原因在上面已經說了,
2. 所有支持依賴注入的類都要支持此方法,
首先,我們可以看到這個注解是在javax.annotation包下的,也就是java拓展包定義的注解,并不是spring定義的,但至于為什么不在java包下,是因為java語言的元老們認為這個東西并不是java核心需要的工具,因此就放到擴展包里(javax中的x就是extension的意思),而spring是支持依賴注入的,因此spring必須要自己來實作@PostConstruct的功能,
3. 檔案中說一個類只能有一個方法加此注解,但實際測驗中,我在一個類中多個方法加了此注解,并沒有報錯,而且都執行了,我用的是 Spring Boot 框架,
再往下看,這個注解有一些使用條件,挑一些重點的說一下

PostConstruct注釋規則
1. 除了攔截器這個特殊情況以外,其他情況都不允許有引數,否則spring框架會報IllegalStateException;而且回傳值要是void,但實際也可以有回傳值,至少不會報錯,只會忽略
2. 方法隨便你用什么權限來修飾,public、protected、private都可以,反正功能是由反射來實作
3. 方法不可以是static的,但可以是final的
所以,綜上所述,在spring專案中,在一個bean的初始化程序中,方法執行先后順序為
Constructor > @Autowired > @PostConstruct
先執行完構造方法,再注入依賴,最后執行初始化操作,所以這個注解就避免了一些需要在構造方法里使用依賴組件的尷尬,
以上是對@PostConstruct的簡單介紹,下面會從spring原始碼分析其具體實作原理,Spring 最常用的 7 大類注解這篇推薦看下,
PostConstruct實作原理
spring遵守了JSR-250標準,實作了javax.annotation包里面的各種注解功能,首先我們在GitHub下載spring-framework原始碼,我下的是5.0.x分支代碼,匯入到idea中,下面就開始動手分析,
首先代碼中搜索"import javax.annotation.PostConstruct",慶幸的是只有CommonAnnotationBeanPostProcessor這一個類有參考PostConstruct類,看名字八九不離十就是它了,它是在org.springframework.context.annotation包下,大致介紹如下:

CommonAnnotationBeanPostProcessor
看來沒什么營養,只是一些簡單介紹說明了我們在什么版本,基于什么標準,實作了這幾個注解,那么看代碼,

CommonAnnotationBeanPostProcessor構造方法
看來只有CommonAnnotationBeanPostProcessor的構造方法使用了這個注解,宣告了這個BeanPostProcessor要支持PostConstruct初始化注解,
關注微信公眾號:Java技術堆疊,在后臺回復:Spring,可以獲取我整理的 N 篇最新 Spring 教程,都是干貨,
跟進去setInitAnnotationType這個方法,是父類InitDestroyAnnotationBeanPostProcessor中的方法,只是簡單的將PostConstruct.class賦值給成員變數initAnnotationType,
那么誰去使用了這個變數,再次意外的發現,只有buildLifecycleMetadata一個方法使用了這個變數,

buildLifecycleMetadata方法
這個方法做的事情也很簡單,輸入一個類,檢查它或者它的祖先類是否有初始化方法以及銷毀方法,如果有,把這些資訊封裝成一個LifecycleMetadata類,里面大概資訊就是類名、初始化和銷毀方法串列,方便bean注冊或消亡的時候去呼叫,
偶然看到LifecycleMetadata中初始化方法串列是List

LifecycleElement構造方法
這是題外話了,接著看buildLifecycleMetadata方法中while回圈里,不斷遍歷父類,找PostConstruct注解,每找完一個父類,往initMethods中累加,最后注冊到與這個bean相應的initMethods中,
前文說了 “我在一個類中多個方法加了此注解,并沒有報錯,而且都執行了”,看過上述代碼后就知道了,spring根本沒有按照javax的要求做限制,可能認為沒必要吧,那么多個PostConstruct注解或父類也有此注解,他們是什么順序執行的呢?
1. 首先父類的初始化方法是先于子類的先執行,但注意不要被子類方法重寫,那父類初始化方法就不會執行了,因為中間有一步是用LinkedHashSet存了method的名字,
2. 同一類內,多個PostConstruct注解方法不是按宣告順序執行的,看了一下代碼邏輯,雖然存盤方法的集合都是有序集合,看起來應該可以順序執行,但實際上是以一種非常詭異的順序來執行,
為了看一下spring的初始化程序,在application.properties中設定trace=true,在控制臺看debug日志后發現,跟存盤方法的集合沒宣告關系,最開始反射取方法的時候順序就打亂了,
罪魁禍首就是ReflectionUtils.doWithLocalMethods 這個方法啦!看了一下JDK的API,發現它強調了Class類不能保證getDeclaredMethods()的順序,因為JVM有權在編譯時,自行決定類成員的順序,
好了,所以現在知道了buildLifecycleMetadata這個方法,就是將bean生命周期的元資料組裝一下回傳,在類中也只有下面一個方法呼叫了

findLifecycleMetadata方法
它把bean的LifecycleMetadata放到一個ConcurrentHashMap保存,然后再往上找,就是AbstractAutowireCapableBeanFactory對bean的初始化和消亡操作了,
在注冊完之后就會invoke方法,這是另外一個話題了,此處不再過多介紹,所以本文到此為止,
綜上,通過原始碼來學習還是很高效的嘛,主要是學習大神們的代碼精髓,
關注微信公眾號:Java技術堆疊,在后臺回復:Spring,可以獲取我整理的 N 篇最新 Spring 教程,都是干貨,
近期熱文推薦:
1.Java 15 正式發布, 14 個新特性,重繪你的認知!!
2.終于靠開源專案弄到 IntelliJ IDEA 激活碼了,真香!
3.我用 Java 8 寫了一段邏輯,同事直呼看不懂,你試試看,,
4.吊打 Tomcat ,Undertow 性能很炸!!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/195910.html
標籤:Java
上一篇:群暉DS218+部署mysql
