成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一張長圖透徹理解 SpringBoot 啟動原理

開發 前端
SpringBoot 會在Spring完全啟動完成后,才開啟Http流量。這給了我們啟示:應該在Spring啟動完成后開啟入口流量。Rpc和 MQ流量 也應該如此,所以建議大家 在 SmartLifecype? 或者 ContextRefreshedEvent 等位置 注冊服務,開啟流量。

雖然Java程序員大部分工作都是CRUD,但是工作中常用的中間件必須和Spring集成,如果不知道Spring的原理,很難理解這些中間件和框架的原理。

一張長圖透徹解釋 Spring啟動順序

圖片圖片

測試對Spring啟動原理的理解程度

我舉個例子,測試一下,你對Spring啟動原理的理解程度。

  • Rpc框架和Spring的集成問題。Rpc框架何時注冊暴露服務,在哪個Spring擴展點注冊呢?init-method 中行不行?
  • MQ 消費組和Spring的集成問題。MQ消費者何時開始消費,在哪個Spring擴展點”注冊“自己?init-method 中行不行?
  • SpringBoot 集成Tomcat問題。如果出現已開啟Http流量,Spring還未啟動完成,怎么辦?Tomcat何時開啟端口,對外服務?

SpringBoot項目常見的流量入口無外乎 Rpc、Http、MQ 三種方式。一名合格的架構師必須精通服務的入口流量何時開啟,如何正確開啟?最近我遇到的兩次線上故障都和Spring啟動過程相關。

故障的具體表現是:Kafka消費組已經開始消費,已開啟流量,然而Spring 還未啟動完成。因為業務代碼中使用的Spring Event事件訂閱組件還未啟動(訂閱者還未注冊到Spring),所以處理異常,出了線上故障。根本原因是————項目在錯誤的時機開啟 MQ 流量,然而Spring還未啟動完成,導致出現故障。

正確的做法是:項目在Spring啟動完成后開啟入口流量,然而我司的Kafka消費組在Spring init-method bean 實例化階段就開啟了流量,導致故障發生。

接下來,我再次拋出 11 個問題,說明這個問題————深入理解Spring啟動原理的重要性。

  1. Spring還未完全啟動,在 PostConstruct 中調用 getBeanByAnnotation 能否獲得準確的結果?
  2. 項目應該如何監聽 Spring 的啟動就緒事件?
  3. 項目如何監聽Spring 刷新事件?
  4. Spring就緒事件和刷新事件的執行順序和區別?
  5. Http 流量入口何時啟動完成?
  6. 項目中在 init-method 方法中注冊 Rpc 是否合理?什么是合理的時機?
  7. 項目中在 init-method 方法中注冊 MQ 消費組是否合理?什么是合理的時機?
  8. PostConstruct 中方法依賴ApplicationContextAware拿到 ApplicationContext,兩者的順序誰先誰后?是否會出現空指針!
  9. init-method、PostConstruct、afterPropertiesSet 三個方法的執行順序?
  10. 有兩個 Bean聲明了初始化方法。A使用 PostConstruct注解聲明,B使用 init-method 聲明。Spring一定先執行 A 的PostConstruct 方法嗎?
  11. Spring 何時裝配Autowire屬性,PostConstruct 方法中引用 Autowired 字段什么場景會空指針?

精通Spring 啟動原理,以上問題則迎刃而解。接下來,大家一起學習Spring的啟動原理,看看Spring的擴展點分別在何時執行。

一起數數 Spring啟動過程的擴展點有幾個?

Spring的擴展點極多,這里為了講清楚啟動原理,所以只列舉和啟動過程有關的擴展點。

  1. BeanFactoryAware 可在Bean 中獲取 BeanFactory 實例
  2. ApplicationContextAware 可在Bean 中獲取 ApplicationContext 實例
  3. BeanNameAware  可以在Bean中得到它在IOC容器中的Bean的實例的名字。
  4. ApplicationListener 可監聽 ContextRefreshedEvent等。
  5. CommandLineRunner 整個項目啟動完畢后,自動執行
  6. SmartLifecycle#start 在Spring Bean實例化完成后,執行start 方法。
  7. 使用@PostConstruct注解,用于Bean實例初始化
  8. 實現InitializingBean接口,用于Bean實例初始化
  9. xml 中聲明 init-method 方法,用于Bean實例初始化
  10. Configuration 配置類 通過@Bean注解 注冊Bean到Spring
  11. BeanPostProcessor 在Bean的初始化前后,植入擴展點!
  12. BeanFactoryPostProcessor 在BeanFactory創建后植入 擴展點!

通過打印日志學習Spring的執行順序

首先我們先通過代碼實驗,驗證一下以上擴展點的執行順序。

1.聲明 TestSpringOrder 分別繼承以下接口,并且在接口方法實現中,日志打印該接口的名稱。

public class TestSpringOrder implements
      ApplicationContextAware,
      BeanFactoryAware, 
      InitializingBean, 
      SmartLifecycle, 
      BeanNameAware, 
      ApplicationListener<ContextRefreshedEvent>, 
      CommandLineRunner,
      SmartInitializingSingleton {
@Override
public void afterPropertiesSet() throws Exception {
   log.error("啟動順序:afterPropertiesSet");
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
   log.error("啟動順序:setApplicationContext");
}

2.TestSpringOrder 使用 PostConstruct注解初始化,聲明 init-method方法初始化。

@PostConstruct
public void postConstruct() {
   log.error("啟動順序:post-construct");
}

public void initMethod() {
   log.error("啟動順序:init-method");
}

3.新建 TestSpringOrder2 

public class TestSpringOrder2 implements
         BeanPostProcessor, 
         BeanFactoryPostProcessor {
   @Override
   public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      log.error("啟動順序:BeanPostProcessor postProcessBeforeInitialization beanName:{}", beanName);
      return bean;
   }

   @Override
   public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      log.error("啟動順序:BeanPostProcessor postProcessAfterInitialization beanName:{}", beanName);
      return bean;
   }

   @Override
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      log.error("啟動順序:BeanFactoryPostProcessor postProcessBeanFactory ");
   }
}

執行以上代碼后,可以在日志中看到啟動順序!

實際的執行順序

2023-11-25 18:10:53,748 [main] ERROR (TestSpringOrder3:37) - 啟動順序:BeanFactoryPostProcessor postProcessBeanFactory 
2023-11-25 18:10:59,299 [main] ERROR (TestSpringOrder:53) - 啟動順序:構造函數 TestSpringOrder
2023-11-25 18:10:59,316 [main] ERROR (TestSpringOrder:127) - 啟動順序: Autowired
2023-11-25 18:10:59,316 [main] ERROR (TestSpringOrder:129) - 啟動順序:setBeanName
2023-11-25 18:10:59,316 [main] ERROR (TestSpringOrder:111) - 啟動順序:setBeanFactory
2023-11-25 18:10:59,316 [main] ERROR (TestSpringOrder:121) - 啟動順序:setApplicationContext
2023-11-25 18:10:59,316 [main] ERROR (TestSpringOrder3:25) - 啟動順序:BeanPostProcessor postProcessBeforeInitialization beanName:testSpringOrder
2023-11-25 18:10:59,316 [main] ERROR (TestSpringOrder:63) - 啟動順序:post-construct
2023-11-25 18:10:59,317 [main] ERROR (TestSpringOrder:116) - 啟動順序:afterPropertiesSet
2023-11-25 18:10:59,317 [main] ERROR (TestSpringOrder:46) - 啟動順序:init-method
2023-11-25 18:10:59,320 [main] ERROR (TestSpringOrder3:31) - 啟動順序:BeanPostProcessor postProcessAfterInitialization beanName:testSpringOrder
2023-11-25 18:17:21,563 [main] ERROR (SpringOrderConfiguartion:21) - 啟動順序: @Bean 注解方法執行
2023-11-25 18:17:21,668 [main] ERROR (TestSpringOrder:58) - 啟動順序:SmartInitializingSingleton
2023-11-25 18:17:21,675 [main] ERROR (TestSpringOrder:74) - 啟動順序:start
2023-11-25 18:17:23,508 [main] ERROR (TestSpringOrder:68) - 啟動順序:ContextRefreshedEvent
2023-11-25 18:17:23,574 [main] ERROR (TestSpringOrder:79) - 啟動順序:CommandLineRunner

我通過在以上擴展點 添加 debug 斷點,調試代碼,整理出 Spring啟動原理的 長圖。過程省略…………

一張長圖透徹解釋 Spring啟動順序

圖片圖片

實例化和初始化的區別

new TestSpringOrder():new 創建對象實例,即為實例化一個對象;執行該Bean的 init-method 等方法 為初始化一個Bean。注意初始化和實例化的區別。

Spring 重要擴展點的啟動順序

1.BeanFactoryPostProcessor

BeanFactory初始化之后,所有的Bean定義已經被加載,但Bean實例還沒被創建(不包括BeanFactoryPostProcessor類型)。Spring IoC容器允許BeanFactoryPostProcessor讀取配置元數據,修改bean的定義,Bean的屬性值等。

2.實例化Bean

Spring 調用java反射API 實例化 Bean。等同于 new TestSpringOrder();

3.Autowired 裝配依賴

Autowired是 借助于 AutowiredAnnotationBeanPostProcessor 解析 Bean 的依賴,裝配依賴。如果被依賴的Bean還未初始化,則先初始化 被依賴的Bean。在 Bean實例化完成后,Spring將首先裝配Bean依賴的屬性。

4.BeanNameAware

setBeanName。

5.BeanFactoryAware

setBeanFactory。

6.ApplicationContextAware setApplicationContext

在Bean實例化前,會率先設置Aware接口,例如 BeanNameAware BeanFactoryAware  ApplicationContextAware 等。

7.BeanPostProcessor postProcessBeforeInitialization

如果我想在 bean初始化方法前后要添加一些自己邏輯處理。可以提供 BeanPostProcessor接口實現類,然后注冊到Spring IoC容器中。在此接口中,可以創建Bean的代理,甚至替換這個Bean。

8.PostConstruct 執行

接下來 Spring會依次調用 Bean實例初始化的 三大方法。

9.InitializingBean

afterPropertiesSet。

10.init-method

方法執行。

11.BeanPostProcessor postProcessAfterInitialization

在 Spring 對Bean的初始化方法執行完成后,執行該方法。

12.其他Bean 實例化和初始化

Spring 會循環初始化Bean。直至所有的單例Bean都完成初始化。

13.所有單例Bean 初始化完成后

14.SmartInitializingSingleton Bean實例化后置處理

該接口的執行時機在所有的單例Bean執行完成后。例如Spring 事件訂閱機制的 EventListener注解,所有的訂閱者都是在這個位置被注冊進 Spring的。而在此之前,Spring Event訂閱機制還未初始化完成。所以如果有 MQ、Rpc 入口流量在此之前開啟,Spring Event就可能出問題!

所以Http、MQ、Rpc 入口流量必須在 SmartInitializingSingleton 之后開啟流量。

15.Spring 提供的擴展點,在所有單例Bean的 EventListener等組件全部啟動完成后,即Spring啟動完成,則執行 start 方法。在這個位置適合開啟入口流量!

Http、MQ、Rpc 入口流量適合 在 SmartLifecyle 中開啟

16.發布 ContextRefreshedEvent 方法

該事件會執行多次,在 Spring Refresh 執行完成后,就會發布該事件!

17.注冊和初始化 Spring MVC

SpringBoot 應用,在父級 Spring啟動完成后,會嘗試啟動 內嵌式 tomcat容器。在此之前,SpringBoot會初始化 SpringMVC 和注冊DispatcherServlet到Web容器。

18.Tomcat/Jetty 容器開啟端口

SpringBoot 調用內嵌式容器,會開啟并監聽端口,此時Http流量就開啟了。

19.應用啟動完成后,執行 CommandLineRunner

SpringBoot 特有的機制,待所有的完全執行完成后,會執行該接口 run方法。值得一提的是,由于此時Http流量已經開啟,如果此時進行本地緩存初始化、預熱緩存等,稍微有些晚了!在這個間隔期,可能緩存還未就緒!

所以預熱緩存的時機應該發生在 入口流量開啟之前,比較合適的機會是在 Bean初始化的階段。雖然 在Bean初始化時 Spring尚未完成啟動,但是調用 Bean預熱緩存也是可以的。但是注意:不要在 Bean初始化時 使用 Spring Event,因為它還未完成初始化 。

回答 關于 Spring 啟動原理的若干問題

1.init-method、PostConstruct、afterPropertiesSet 三個方法的執行順序。

回答:PostConstruct,afterPropertiesSet,init-method

2.有兩個 Bean聲明了初始化方法。A使用 PostConstruct注解聲明,B使用 init-method 聲明。Spring一定先執行 A 的PostConstruct 方法嗎?

回答:Spring 會循環初始化Bean實例,初始化完成1個Bean,再初始化下一個Bean。A、B兩個Bean的初始化順序不確定,誰先誰后不確定。無法保證 A 的PostConstruct 一定先執行。除非使用 Order注解,聲明Bean的初始化順序!

3.Spring 何時裝配Autowire屬性,PostConstruct方法中引用 Autowired 字段是否會空指針?

Autowired裝配依賴發生在 PostConstruct之前,不會出現空指針!

4.PostConstruct 中方法依賴ApplicationContextAware拿到 ApplicationContext,兩者的順序誰先誰后?是否會出現空指針!

ApplicationContextAware 會先執行,不會出現空指針!但是當Autowired沒有找到對應的依賴,并且聲明了非強制依賴時,該字段會為空,有潛在空指針風險。

5.項目應該如何監聽 Spring 的啟動就緒事件。

通過SmartLifecyle start方法,監聽Spring就緒 。適合在此開啟入口流量!

6.項目如何監聽Spring 刷新事件。

監聽 Spring Event ContextRefreshedEvent

7.Spring就緒事件和刷新事件的執行順序和區別。

Spring就緒事件會先于 刷新事件。兩者都可能多次執行,要確保方法的冪等處理,避免重復注冊問題

8.Http 流量入口何時啟動完成。

SpringBoot 最后階段,啟動完成Spring 上下文,才開啟Http入口流量,此時 SmartLifecycle#start 已執行。所有單例Bean和SpringEvent等組件都已經就緒!

9.項目中在 init-method 方法中注冊 Rpc是否合理?什么是合理的時機?

init 開啟Rpc流量非常不合理。因為Spring尚未啟動完成,包括 Spring Event尚未就緒!

10.項目中在 init-method 方法中注冊 MQ消費組是否合理?什么是合理的時機?

init 開啟 MQ 流量非常不合理。因為Spring尚未啟動完成,包括 Spring Event尚未就緒!

11.Spring還未完全啟動,在 PostConstruct 中調用 getBeanByAnnotation能否獲得準確的結果?

雖然未啟動完成,但是Spring執行該getBeanByAnnotation方法時,會率先檢查 Bean定義,如果Bean定義對應的 Bean尚未初始化,則初始化這些Bean。所以即便是Spring初始化過程中調用,調用結果是準確的。

源碼級別介紹

SmartInitializingSingleton 接口的執行位置

下圖代碼說明了,Spring在初始化全部 單例Bean以后,會執行 SmartInitializingSingleton 接口。

圖片圖片

Autowired 何時裝配Bean的依賴

在Bean實例化之后,但初始化之前,AutowiredAnnotationBeanPostProcessor 會注入Autowired字段。

圖片圖片

SpringBoot 何時開啟Http端口

下圖代碼中可以看到,SpringBoot會首先啟動 Spring上下文,完成后才啟動 嵌入式Web容器,初始化SpringMVC,監聽端口。

圖片圖片

Spring 初始化Bean的關鍵代碼

下圖我加了注釋,Spring初始化Bean的關鍵代碼,全在 這個方法里,感興趣的可以自行查閱代碼 。

AbstractAutowireCapableBeanFactory#initializeBean。

Spring CommandLineRunner 執行位置

Spring Boot外部,當啟動完Spring上下文以后,最后才啟動 CommandLineRunner。

總結

SpringBoot 會在Spring完全啟動完成后,才開啟Http流量。這給了我們啟示:應該在Spring啟動完成后開啟入口流量。Rpc和 MQ流量 也應該如此,所以建議大家 在 SmartLifecype 或者 ContextRefreshedEvent 等位置 注冊服務,開啟流量。

例如 Spring Cloud  Eureka 服務發現組件,就是在 SmartLifecype中注冊服務的!

責任編輯:武曉燕 來源: 一安未來
相關推薦

2021-02-07 09:01:10

Java并發編程

2016-01-05 11:38:59

Linux內核運行

2015-03-10 10:15:27

AppleWatch開發Swift

2019-09-11 10:12:12

華為

2020-11-27 06:28:55

Spring循環依賴

2015-09-14 09:07:15

Java多線程

2018-02-13 14:56:24

戴爾

2020-09-12 16:45:49

Git

2025-03-11 10:58:00

2022-08-19 14:46:16

視覺框架

2015-06-24 10:51:10

iOS學習流程

2021-09-29 11:30:01

大數據技術架構

2015-09-23 10:04:03

開放數據

2015-10-29 15:09:32

信息圖數據

2023-09-05 08:53:51

2018-05-18 18:09:44

人工智能

2024-05-07 08:49:45

微服務架構模式

2013-12-16 10:59:52

WiFi上鎖WiFi被盜

2022-03-31 11:41:02

DDoS網絡安全關鍵信息基礎設施
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一级看片免费视频 | 在线看亚洲 | 免费视频一区二区三区在线观看 | 青青久视频| 国产精品国产成人国产三级 | 久久久久久成人 | 日韩精品一区二区三区免费视频 | 在线观看免费毛片 | 羞羞视频网站在线观看 | 国产日韩欧美一区 | 亚洲国产精品一区二区三区 | 97精品久久 | 欧美综合久久 | 午夜精品影院 | 国产色| 亚洲一区二区久久 | 免费一级网站 | 国产美女在线观看 | 午夜性视频 | 欧美日韩国产一区二区三区 | 久久精品久久久久久 | 在线观看中文字幕视频 | 久久久一区二区三区 | 精品亚洲一区二区三区 | 狠狠操狠狠干 | 亚洲一区在线日韩在线深爱 | 亚洲精品成人av久久 | 日韩视频免费在线 | 亚洲一二三视频 | 国产在线观看一区二区 | 国产一区二区美女 | 三级黄色大片网站 | 午夜免费视频观看 | 欧美福利影院 | 天天操天天射天天 | 欧美在线资源 | 精品欧美一区二区三区久久久 | 天堂精品 | 成人激情视频在线播放 | 亚洲午夜精品在线观看 | h在线播放 |