淺談SpringBoot中的Bean初始化方法 @PostConstruct
註解說明
- 使用註解: @PostConstruct
- 效果:在Bean初始化之後(構造方法和@Autowired之後)執行指定操作。經常用在將構造方法中的動作延遲。
- 備註:Bean初始化時候的執行順序: 構造方法 -> @Autowired -> @PostConstruct
代碼示例
註解示例
@Component public class PostConstructTest1 { @Autowired PostConstructTest2 postConstructTest2; public PostConstructTest1() { // postConstructTest2.hello(); } @PostConstruct public void init() { // some init function } }
在Bean的初始化操作中,有時候會遇到調用其他Bean的時候報空指針錯誤。這時候就可以將調用另一個Bean的方法這個操作放到@PostConstruct註解的方法中,將其延遲執行。
錯誤示例
@Component public class PostConstructTest1 { @Autowired PostConstructTest2 postConstructTest2; public PostConstructTest1() { postConstructTest2.hello(); } }
@Component public class PostConstructTest2 { public void hello() { System.out.println("hello, i am PostConstructTest2"); } }
正確示例
@Component public class PostConstructTest1 { @Autowired PostConstructTest2 postConstructTest2; public PostConstructTest1() { postConstructTest2.hello(); } }
@Component public class PostConstructTest1 { @Autowired PostConstructTest2 postConstructTest2; public PostConstructTest1() { // postConstructTest2.hello(); } @PostConstruct public void init() { postConstructTest2.hello(); } }
SpringBoot @PostConstruct雖好,也要慎用
做過SpringBoot開發的話,肯定對@PostConstruct比較熟悉。在一個Bean組件中,標記瞭@PostConstruct的方法會在Bean構造完成後自動執行方法的邏輯。
1 問題的產生
先說下SpringBoot中Bean的加載過程,簡單點說就是SpringBoot會把標記瞭Bean相關註解(例如@Component、@Service、@Repository等)的類或接口自動初始化全局的單一實例,如果標記瞭初始化順序會按照用戶標記的順序,否則按照默認順序初始化。在初始化的過程中,執行完一個Bean的構造方法後會執行該Bean的@PostConstruct方法(如果有),然後初始化下一個Bean。
那麼: 如果@PostConstruct方法內的邏輯處理時間較長,就會增加SpringBoot應用初始化Bean的時間,進而增加應用啟動的時間。因為隻有在Bean初始化完成後,SpringBoot應用才會打開端口提供服務,所以在此之前,應用不可訪問。
2 案例模擬
為瞭模擬上面說的情況,在SpringBoot項目中建兩個組件類ComponentOne和ComponentTwo。耗時的初始化邏輯放在ComponentOne中,並設置ComponentOne的初始化順序在ComponentTwo之前。完整代碼如下:
@Component @Order(Ordered.HIGHEST_PRECEDENCE) public class ComponentOne { private Logger logger = LoggerFactory.getLogger(this.getClass()); public ComponentOne() { this.logger.info("ComponentOne 初始化完成"); } @PostConstruct public void init() { this.logger.info("ComponentOne 模擬耗時邏輯開始"); try { //這裡休眠5秒模擬耗時邏輯 Thread.sleep(1000 * 5); } catch (InterruptedException e) { logger.info("模擬邏輯耗時失敗", e); } this.logger.info("ComponentOne 模擬耗時邏輯完成"); } }
@Component @Order(Ordered.HIGHEST_PRECEDENCE + 1) public class ComponentTwo { private Logger logger = LoggerFactory.getLogger(this.getClass()); public ComponentTwo() { this.logger.info("ComponentTwo 初始化完成"); } @PostConstruct public void init() { this.logger.info("ComponentTwo 初始化完成後處理"); } }
啟動應用,初始化部分日志如下:
3 總結
所以,如果應用有一些初始化操作,有以下幾點建議:
- 輕量的邏輯可放在Bean的@PostConstruct方法中
- 耗時長的邏輯如果放在@PostConstruct方法中,可使用獨立線程執行
- 初始化操作放在CommandLineRunner或ApplicationRunner的實現組件中
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- 使用Spring啟動時運行自定義業務
- Spring Boot中單例類實現對象的註入方式
- 解決springboot遇到autowire註入為null的問題
- 基於@PostConstruct註解的使用,解決向靜態變量註入值
- 如何在Netty中註解使用Service或者Mapper