偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

一張長(zhǎng)圖透徹理解 SpringBoot 啟動(dòng)原理

開(kāi)發(fā) 前端
SpringBoot 會(huì)在Spring完全啟動(dòng)完成后,才開(kāi)啟Http流量。這給了我們啟示:應(yīng)該在Spring啟動(dòng)完成后開(kāi)啟入口流量。Rpc和 MQ流量 也應(yīng)該如此,所以建議大家 在 SmartLifecype? 或者 ContextRefreshedEvent 等位置 注冊(cè)服務(wù),開(kāi)啟流量。

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

一張長(zhǎng)圖透徹解釋 Spring啟動(dòng)順序

圖片圖片

測(cè)試對(duì)Spring啟動(dòng)原理的理解程度

我舉個(gè)例子,測(cè)試一下,你對(duì)Spring啟動(dòng)原理的理解程度。

  • Rpc框架和Spring的集成問(wèn)題。Rpc框架何時(shí)注冊(cè)暴露服務(wù),在哪個(gè)Spring擴(kuò)展點(diǎn)注冊(cè)呢?init-method 中行不行?
  • MQ 消費(fèi)組和Spring的集成問(wèn)題。MQ消費(fèi)者何時(shí)開(kāi)始消費(fèi),在哪個(gè)Spring擴(kuò)展點(diǎn)”注冊(cè)“自己?init-method 中行不行?
  • SpringBoot 集成Tomcat問(wèn)題。如果出現(xiàn)已開(kāi)啟Http流量,Spring還未啟動(dòng)完成,怎么辦?Tomcat何時(shí)開(kāi)啟端口,對(duì)外服務(wù)?

SpringBoot項(xiàng)目常見(jiàn)的流量入口無(wú)外乎 Rpc、Http、MQ 三種方式。一名合格的架構(gòu)師必須精通服務(wù)的入口流量何時(shí)開(kāi)啟,如何正確開(kāi)啟?最近我遇到的兩次線上故障都和Spring啟動(dòng)過(guò)程相關(guān)。

故障的具體表現(xiàn)是:Kafka消費(fèi)組已經(jīng)開(kāi)始消費(fèi),已開(kāi)啟流量,然而Spring 還未啟動(dòng)完成。因?yàn)闃I(yè)務(wù)代碼中使用的Spring Event事件訂閱組件還未啟動(dòng)(訂閱者還未注冊(cè)到Spring),所以處理異常,出了線上故障。根本原因是————項(xiàng)目在錯(cuò)誤的時(shí)機(jī)開(kāi)啟 MQ 流量,然而Spring還未啟動(dòng)完成,導(dǎo)致出現(xiàn)故障。

正確的做法是:項(xiàng)目在Spring啟動(dòng)完成后開(kāi)啟入口流量,然而我司的Kafka消費(fèi)組在Spring init-method bean 實(shí)例化階段就開(kāi)啟了流量,導(dǎo)致故障發(fā)生。

接下來(lái),我再次拋出 11 個(gè)問(wèn)題,說(shuō)明這個(gè)問(wèn)題————深入理解Spring啟動(dòng)原理的重要性。

  1. Spring還未完全啟動(dòng),在 PostConstruct 中調(diào)用 getBeanByAnnotation 能否獲得準(zhǔn)確的結(jié)果?
  2. 項(xiàng)目應(yīng)該如何監(jiān)聽(tīng) Spring 的啟動(dòng)就緒事件?
  3. 項(xiàng)目如何監(jiān)聽(tīng)Spring 刷新事件?
  4. Spring就緒事件和刷新事件的執(zhí)行順序和區(qū)別?
  5. Http 流量入口何時(shí)啟動(dòng)完成?
  6. 項(xiàng)目中在 init-method 方法中注冊(cè) Rpc 是否合理?什么是合理的時(shí)機(jī)?
  7. 項(xiàng)目中在 init-method 方法中注冊(cè) MQ 消費(fèi)組是否合理?什么是合理的時(shí)機(jī)?
  8. PostConstruct 中方法依賴ApplicationContextAware拿到 ApplicationContext,兩者的順序誰(shuí)先誰(shuí)后?是否會(huì)出現(xiàn)空指針!
  9. init-method、PostConstruct、afterPropertiesSet 三個(gè)方法的執(zhí)行順序?
  10. 有兩個(gè) Bean聲明了初始化方法。A使用 PostConstruct注解聲明,B使用 init-method 聲明。Spring一定先執(zhí)行 A 的PostConstruct 方法嗎?
  11. Spring 何時(shí)裝配Autowire屬性,PostConstruct 方法中引用 Autowired 字段什么場(chǎng)景會(huì)空指針?

精通Spring 啟動(dòng)原理,以上問(wèn)題則迎刃而解。接下來(lái),大家一起學(xué)習(xí)Spring的啟動(dòng)原理,看看Spring的擴(kuò)展點(diǎn)分別在何時(shí)執(zhí)行。

一起數(shù)數(shù) Spring啟動(dòng)過(guò)程的擴(kuò)展點(diǎn)有幾個(gè)?

Spring的擴(kuò)展點(diǎn)極多,這里為了講清楚啟動(dòng)原理,所以只列舉和啟動(dòng)過(guò)程有關(guān)的擴(kuò)展點(diǎn)。

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

通過(guò)打印日志學(xué)習(xí)Spring的執(zhí)行順序

首先我們先通過(guò)代碼實(shí)驗(yàn),驗(yàn)證一下以上擴(kuò)展點(diǎn)的執(zhí)行順序。

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

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

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

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

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

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

3.新建 TestSpringOrder2 

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

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

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

執(zhí)行以上代碼后,可以在日志中看到啟動(dòng)順序!

實(shí)際的執(zhí)行順序

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

我通過(guò)在以上擴(kuò)展點(diǎn) 添加 debug 斷點(diǎn),調(diào)試代碼,整理出 Spring啟動(dòng)原理的 長(zhǎng)圖。過(guò)程省略…………

一張長(zhǎng)圖透徹解釋 Spring啟動(dòng)順序

圖片圖片

實(shí)例化和初始化的區(qū)別

new TestSpringOrder():new 創(chuàng)建對(duì)象實(shí)例,即為實(shí)例化一個(gè)對(duì)象;執(zhí)行該Bean的 init-method 等方法 為初始化一個(gè)Bean。注意初始化和實(shí)例化的區(qū)別。

Spring 重要擴(kuò)展點(diǎn)的啟動(dòng)順序

1.BeanFactoryPostProcessor

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

2.實(shí)例化Bean

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

3.Autowired 裝配依賴

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

4.BeanNameAware

setBeanName。

5.BeanFactoryAware

setBeanFactory。

6.ApplicationContextAware setApplicationContext

在Bean實(shí)例化前,會(huì)率先設(shè)置Aware接口,例如 BeanNameAware BeanFactoryAware  ApplicationContextAware 等。

7.BeanPostProcessor postProcessBeforeInitialization

如果我想在 bean初始化方法前后要添加一些自己邏輯處理??梢蕴峁?nbsp;BeanPostProcessor接口實(shí)現(xiàn)類,然后注冊(cè)到Spring IoC容器中。在此接口中,可以創(chuàng)建Bean的代理,甚至替換這個(gè)Bean。

8.PostConstruct 執(zhí)行

接下來(lái) Spring會(huì)依次調(diào)用 Bean實(shí)例初始化的 三大方法。

9.InitializingBean

afterPropertiesSet。

10.init-method

方法執(zhí)行。

11.BeanPostProcessor postProcessAfterInitialization

在 Spring 對(duì)Bean的初始化方法執(zhí)行完成后,執(zhí)行該方法。

12.其他Bean 實(shí)例化和初始化

Spring 會(huì)循環(huán)初始化Bean。直至所有的單例Bean都完成初始化。

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

14.SmartInitializingSingleton Bean實(shí)例化后置處理

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

所以Http、MQ、Rpc 入口流量必須在 SmartInitializingSingleton 之后開(kāi)啟流量。

15.Spring 提供的擴(kuò)展點(diǎn),在所有單例Bean的 EventListener等組件全部啟動(dòng)完成后,即Spring啟動(dòng)完成,則執(zhí)行 start 方法。在這個(gè)位置適合開(kāi)啟入口流量!

Http、MQ、Rpc 入口流量適合 在 SmartLifecyle 中開(kāi)啟

16.發(fā)布 ContextRefreshedEvent 方法

該事件會(huì)執(zhí)行多次,在 Spring Refresh 執(zhí)行完成后,就會(huì)發(fā)布該事件!

17.注冊(cè)和初始化 Spring MVC

SpringBoot 應(yīng)用,在父級(jí) Spring啟動(dòng)完成后,會(huì)嘗試啟動(dòng) 內(nèi)嵌式 tomcat容器。在此之前,SpringBoot會(huì)初始化 SpringMVC 和注冊(cè)DispatcherServlet到Web容器。

18.Tomcat/Jetty 容器開(kāi)啟端口

SpringBoot 調(diào)用內(nèi)嵌式容器,會(huì)開(kāi)啟并監(jiān)聽(tīng)端口,此時(shí)Http流量就開(kāi)啟了。

19.應(yīng)用啟動(dòng)完成后,執(zhí)行 CommandLineRunner

SpringBoot 特有的機(jī)制,待所有的完全執(zhí)行完成后,會(huì)執(zhí)行該接口 run方法。值得一提的是,由于此時(shí)Http流量已經(jīng)開(kāi)啟,如果此時(shí)進(jìn)行本地緩存初始化、預(yù)熱緩存等,稍微有些晚了!在這個(gè)間隔期,可能緩存還未就緒!

所以預(yù)熱緩存的時(shí)機(jī)應(yīng)該發(fā)生在 入口流量開(kāi)啟之前,比較合適的機(jī)會(huì)是在 Bean初始化的階段。雖然 在Bean初始化時(shí) Spring尚未完成啟動(dòng),但是調(diào)用 Bean預(yù)熱緩存也是可以的。但是注意:不要在 Bean初始化時(shí) 使用 Spring Event,因?yàn)樗€未完成初始化 。

回答 關(guān)于 Spring 啟動(dòng)原理的若干問(wèn)題

1.init-method、PostConstruct、afterPropertiesSet 三個(gè)方法的執(zhí)行順序。

回答:PostConstruct,afterPropertiesSet,init-method

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

回答:Spring 會(huì)循環(huán)初始化Bean實(shí)例,初始化完成1個(gè)Bean,再初始化下一個(gè)Bean。A、B兩個(gè)Bean的初始化順序不確定,誰(shuí)先誰(shuí)后不確定。無(wú)法保證 A 的PostConstruct 一定先執(zhí)行。除非使用 Order注解,聲明Bean的初始化順序!

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

Autowired裝配依賴發(fā)生在 PostConstruct之前,不會(huì)出現(xiàn)空指針!

4.PostConstruct 中方法依賴ApplicationContextAware拿到 ApplicationContext,兩者的順序誰(shuí)先誰(shuí)后?是否會(huì)出現(xiàn)空指針!

ApplicationContextAware 會(huì)先執(zhí)行,不會(huì)出現(xiàn)空指針!但是當(dāng)Autowired沒(méi)有找到對(duì)應(yīng)的依賴,并且聲明了非強(qiáng)制依賴時(shí),該字段會(huì)為空,有潛在空指針風(fēng)險(xiǎn)。

5.項(xiàng)目應(yīng)該如何監(jiān)聽(tīng) Spring 的啟動(dòng)就緒事件。

通過(guò)SmartLifecyle start方法,監(jiān)聽(tīng)Spring就緒 。適合在此開(kāi)啟入口流量!

6.項(xiàng)目如何監(jiān)聽(tīng)Spring 刷新事件。

監(jiān)聽(tīng) Spring Event ContextRefreshedEvent

7.Spring就緒事件和刷新事件的執(zhí)行順序和區(qū)別。

Spring就緒事件會(huì)先于 刷新事件。兩者都可能多次執(zhí)行,要確保方法的冪等處理,避免重復(fù)注冊(cè)問(wèn)題

8.Http 流量入口何時(shí)啟動(dòng)完成。

SpringBoot 最后階段,啟動(dòng)完成Spring 上下文,才開(kāi)啟Http入口流量,此時(shí) SmartLifecycle#start 已執(zhí)行。所有單例Bean和SpringEvent等組件都已經(jīng)就緒!

9.項(xiàng)目中在 init-method 方法中注冊(cè) Rpc是否合理?什么是合理的時(shí)機(jī)?

init 開(kāi)啟Rpc流量非常不合理。因?yàn)镾pring尚未啟動(dòng)完成,包括 Spring Event尚未就緒!

10.項(xiàng)目中在 init-method 方法中注冊(cè) MQ消費(fèi)組是否合理?什么是合理的時(shí)機(jī)?

init 開(kāi)啟 MQ 流量非常不合理。因?yàn)镾pring尚未啟動(dòng)完成,包括 Spring Event尚未就緒!

11.Spring還未完全啟動(dòng),在 PostConstruct 中調(diào)用 getBeanByAnnotation能否獲得準(zhǔn)確的結(jié)果?

雖然未啟動(dòng)完成,但是Spring執(zhí)行該getBeanByAnnotation方法時(shí),會(huì)率先檢查 Bean定義,如果Bean定義對(duì)應(yīng)的 Bean尚未初始化,則初始化這些Bean。所以即便是Spring初始化過(guò)程中調(diào)用,調(diào)用結(jié)果是準(zhǔn)確的。

源碼級(jí)別介紹

SmartInitializingSingleton 接口的執(zhí)行位置

下圖代碼說(shuō)明了,Spring在初始化全部 單例Bean以后,會(huì)執(zhí)行 SmartInitializingSingleton 接口。

圖片圖片

Autowired 何時(shí)裝配Bean的依賴

在Bean實(shí)例化之后,但初始化之前,AutowiredAnnotationBeanPostProcessor 會(huì)注入Autowired字段。

圖片圖片

SpringBoot 何時(shí)開(kāi)啟Http端口

下圖代碼中可以看到,SpringBoot會(huì)首先啟動(dòng) Spring上下文,完成后才啟動(dòng) 嵌入式Web容器,初始化SpringMVC,監(jiān)聽(tīng)端口。

圖片圖片

Spring 初始化Bean的關(guān)鍵代碼

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

AbstractAutowireCapableBeanFactory#initializeBean。

Spring CommandLineRunner 執(zhí)行位置

Spring Boot外部,當(dāng)啟動(dòng)完Spring上下文以后,最后才啟動(dòng) CommandLineRunner。

總結(jié)

SpringBoot 會(huì)在Spring完全啟動(dòng)完成后,才開(kāi)啟Http流量。這給了我們啟示:應(yīng)該在Spring啟動(dòng)完成后開(kāi)啟入口流量。Rpc和 MQ流量 也應(yīng)該如此,所以建議大家 在 SmartLifecype 或者 ContextRefreshedEvent 等位置 注冊(cè)服務(wù),開(kāi)啟流量。

例如 Spring Cloud  Eureka 服務(wù)發(fā)現(xiàn)組件,就是在 SmartLifecype中注冊(cè)服務(wù)的!

責(zé)任編輯:武曉燕 來(lái)源: 一安未來(lái)
相關(guān)推薦

2021-02-07 09:01:10

Java并發(fā)編程

2016-01-05 11:38:59

Linux內(nèi)核運(yùn)行

2019-09-11 10:12:12

華為

2015-03-10 10:15:27

AppleWatch開(kāi)發(fā)Swift

2020-11-27 06:28:55

Spring循環(huán)依賴

2015-09-14 09:07:15

Java多線程

2015-09-23 10:04:03

開(kāi)放數(shù)據(jù)

2015-10-29 15:09:32

信息圖數(shù)據(jù)

2023-09-05 08:53:51

2018-05-18 18:09:44

人工智能

2024-05-07 08:49:45

微服務(wù)架構(gòu)模式

2013-12-16 10:59:52

WiFi上鎖WiFi被盜

2018-02-13 14:56:24

戴爾

2020-09-12 16:45:49

Git

2015-06-24 10:51:10

iOS學(xué)習(xí)流程

2022-08-19 14:46:16

視覺(jué)框架

2025-03-11 10:58:00

2021-09-29 11:30:01

大數(shù)據(jù)技術(shù)架構(gòu)

2015-07-13 10:23:23

Java圖解
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)