国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

SpringBoot啟動原理詳解(圖文全面總結)

開發 前端
現在我們已經加載了所有的自動化配置類了,但是這些配置類并不是都會生效,具體是否生效,還要看你的項目是否使用了具體的依賴。

雖然我們在日常開發中,Spring Boot 使用非常多,算是目前 Java 開發領域一個標配了,但是小伙伴們仔細想想自己的面試經歷,和 Spring Boot 相關的面試題都有哪些?個人感覺應該是比較少的,Spring Boot 本質上還是曾經 SSM 那一套,只是通過各種 starter 簡化了配置而已,其他都是一模一樣的,所以 Spring Boot 中很多面試題還是得回歸到 Spring 中去解答!當然這并不是說 Spring Boot 中沒什么可問的,Spring Boot 中其實也有一個非常經典的面試題,那就是 Spring Boot 的啟動原理是什么?今天松哥就來和各位小伙伴聊一下這個問題。

其實松哥之前和小伙伴們聊過相關的問題,不過都是零散的,沒有系統梳理過,之前也帶領小伙伴們自定義過一個 starter,相信各位小伙伴對于 starter 的原理也有一定了解,所以今天這篇文章一些過于細節的內容我就不贅述了,大家可以翻看之前的文章。

一、@SpringBootApplication

要說 Spring Boot 的自動化配置,那必須從項目的啟動類 @SpringBootApplication 說起,這是整個 Spring Boot 宇宙的起點,我們先來看下這個注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

}

可以看到,@SpringBootApplication 注解組合了多個常見注解的功能,其中:

  • 前四個是元注解,這里我們不做討論。
  • 第五個 @SpringBootConfiguration 是一個支持配置類的注解,這里我們也不做討論。
  • 第六個 @EnableAutoConfiguration 這個注解就表示開啟自動化配置,這是我們今天要聊得重點。
  • 第七個 @ComponentScan 是一個包掃描注解,為什么 Spring Boot 項目中的 Bean 只要放對位置就會被自動掃描到,和這個注解有關。

別看這里注解多,其實真正由 Spring Boot 提供的注解一共就兩個,分別是 @SpringBootConfiguration 和 @EnableAutoConfiguration 兩個,其他注解在 Spring Boot 出現之前就已經存在多年了。

二、@EnableAutoConfiguration

接下來我們來看看 @EnableAutoConfiguration 是如何實現自動化配置的。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

}

這個注解起關鍵作用的就是兩個東西:

  1. @AutoConfigurationPackage:這個表示自動掃描各種第三方的注解,在之前的文章中松哥已經和大家聊過這個注解的作用了,傳送門:@AutoConfigurationPackage 和 @ComponentScan 有何區別?
  2. @Import 則是在導入 AutoConfigurationImportSelector 配置類,這個配置類里邊就是去加載各種自動化配置類的。

三、AutoConfigurationImportSelector

AutoConfigurationImportSelector 類中的方法比較多,入口的地方則是 process 方法,所以我們這里就從 process 方法開始看起:

@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
 Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
   () -> String.format("Only %s implementations are supported, got %s",
     AutoConfigurationImportSelector.class.getSimpleName(),
     deferredImportSelector.getClass().getName()));
 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
  .getAutoConfigurationEntry(annotationMetadata);
 this.autoConfigurationEntries.add(autoConfigurationEntry);
 for (String importClassName : autoConfigurationEntry.getConfigurations()) {
  this.entries.putIfAbsent(importClassName, annotationMetadata);
 }
}

從類名就可以看出來,跟自動化配置相關的對象是由 AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata); 進行加載的。

當然這里的 getAutoConfigurationEntry 方法實際上就是當前類提供的方法,我們來看下該方法:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
 if (!isEnabled(annotationMetadata)) {
  return EMPTY_ENTRY;
 }
 AnnotationAttributes attributes = getAttributes(annotationMetadata);
 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
 configurations = removeDuplicates(configurations);
 Set<String> exclusions = getExclusions(annotationMetadata, attributes);
 checkExcludedClasses(configurations, exclusions);
 configurations.removeAll(exclusions);
 configurations = getConfigurationClassFilter().filter(configurations);
 fireAutoConfigurationImportEvents(configurations, exclusions);
 return new AutoConfigurationEntry(configurations, exclusions);
}

這里源碼的方法命名都做的不錯,基本上都能做到見名知意,小伙伴們日常開發中,應該向這樣的命名思路看齊。接下來我們就來挨個看一下這里的關鍵方法。

3.1 isEnabled

首先調用 isEnabled 方法去判斷自動化配置到底有沒有開啟,這個主要是因為我們及時在項目中引入了 spring-boot-starter-xxx 之后,我們也可以通過在 application.properties 中配置 spring.boot.enableautoconfiguration=false 來關閉所有的自動化配置。

相關源碼如下:

protected boolean isEnabled(AnnotationMetadata metadata) {
 if (getClass() == AutoConfigurationImportSelector.class) {
  return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
 }
 return true;
}

3.2 getCandidateConfigurations

接下來調用 getCandidateConfigurations 方法去獲取所有候選的自動化配置類,這些候選的自動化配置類主要來自兩個地方:

  1. 在之前的自定義 starter 中松哥和大家聊過,我們需要在 claspath\:META-INF/spring.factories 中定義出來所有的自動化配置類,這是來源一。
  2. Spring Boot 自帶的自動化配置類,這個在之前的 vhr 視頻中也和小伙伴們多次講過,Spring Boot 自帶的自動化配置類位于 spring-boot-autoconfigure-3.0.6.jar!\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中。

相關源碼如下:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
 List<String> configurations = new ArrayList<>(
   SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
 ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
 Assert.notEmpty(configurations,
   "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
     + "are using a custom packaging, make sure that file is correct.");
 return configurations;
}

這里加載到的自動化配置類的全路徑被存入到 configurations 對象中,該對象有兩個獲取的地方:

  1. 調用 SpringFactoriesLoader.loadFactoryNames 方法獲取,這個方法細節我就不帶大家看了,比較簡單,本質上就是去加載 META-INF/spring.factories 文件,這個文件中定義了大量的自動化配置類的全路徑。
  2. 調用 ImportCandidates.load 方法去加載,這個就是加載 spring-boot-autoconfigure-3.0.6.jar!\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中的自動化配置類。

如果這兩個地方都沒有加載到任何自動化配置類,那么就會拋出一個異常。

3.3 removeDuplicates

removeDuplicates 方法表示移除候選自動化配置類中重復的類,移除的思路也很有意思,就用一個 LinkedHashSet 中轉一下就行了,源碼如下:

protected final <T> List<T> removeDuplicates(List<T> list) {
 return new ArrayList<>(new LinkedHashSet<>(list));
}

可以看到這些源碼里有時候一些解決思路也很有意思。

3.4 getExclusions

getExclusions 方法表示需要獲取到所有被排除的自動化配置類,這些被排除的自動化配置類可以從三個地方獲取:

  • 當前注解的 exclude 屬性。
  • 當前注解的 excludeName 屬性。
  • application.properties 配置文件中的 spring.autoconfigure.exclude 屬性。

來看一下相關源碼:

protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
 Set<String> excluded = new LinkedHashSet<>();
 excluded.addAll(asList(attributes, "exclude"));
 excluded.addAll(asList(attributes, "excludeName"));
 excluded.addAll(getExcludeAutoConfigurationsProperty());
 return excluded;
}

跟上面講解的三點剛好對應。

3.5 checkExcludedClasses

這個方法是檢查所有被排除的自動化配置類,由于 Spring Boot 中的自動化配置類可以自定義,并不需要統一實現某一個接口或者統一繼承某一個類,所以在寫排除類的時候,如果寫錯了編譯是校驗不出來的,像下面這種:

@SpringBootApplication(exclude = HelloController.class)
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

由于 HelloController 并不是一個自動化配置類,所以這樣寫項目啟動的時候就會報錯,如下:

圖片圖片

這個異常從哪來的呢?其實就是來自 checkExcludedClasses 方法,我們來看下該方法:

private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
 List<String> invalidExcludes = new ArrayList<>(exclusions.size());
 for (String exclusion : exclusions) {
  if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
   invalidExcludes.add(exclusion);
  }
 }
 if (!invalidExcludes.isEmpty()) {
  handleInvalidExcludes(invalidExcludes);
 }
}
protected void handleInvalidExcludes(List<String> invalidExcludes) {
 StringBuilder message = new StringBuilder();
 for (String exclude : invalidExcludes) {
  message.append("\t- ").append(exclude).append(String.format("%n"));
 }
 throw new IllegalStateException(String.format(
   "The following classes could not be excluded because they are not auto-configuration classes:%n%s",
   message));
}

可以看到,在 checkExcludedClasses 方法中,會首先找到所有位于當前類路徑下但是卻不包含在 configurations 中的所有被排除的自動化配置類,由于 configurations 中的就是所有的自動化配置類了,所以這些不存在于 configurations 中的類都是有問題的,都不是自動化配置類,將這些有問題的類收集起來,存入到 invalidExcludes 變量中,然后再進行額外的處理。

所謂額外的處理就是在 handleInvalidExcludes 方法中拋出異常,前面截圖中的異常就是來自這里。

3.6 removeAll

這個方法就一個任務,就是從 configurations 中移除掉那些被排除的自動化配置類。configurations 本身就是 List 集合,exclusions 則是一個 Set 集合,所以這里直接移除即可。

3.7 filter

現在我們已經加載了所有的自動化配置類了,但是這些配置類并不是都會生效,具體是否生效,還要看你的項目是否使用了具體的依賴。

例如,現在加載的自動化配置里里邊就包含了 RedisAutoConfiguration,這個是自動配置 Redis 的,但是由于我的項目中并沒有使用 Redis,所以這個自動化配置類并不會生效。這個過程就是由 getConfigurationClassFilter().filter(configurations); 來完成的。

先說一個預備知識:

由于我們項目中的自動化配置類特別多,每一個自動化配置類都會依賴別的類,當別的類存在時,這個自動化配置類才會生效,這一堆互相之間的依賴關系,存在于 spring-boot-autoconfigure-3.0.6.jar!/META-INF/spring-autoconfigure-metadata.properties 文件之中,我隨便舉一個該文件中的配置:

  • org.springframework.boot.autoconfigure.amqp.RabbitAnnotationDrivenConfiguration.Cnotallow=org.springframework.amqp.rabbit.annotation.EnableRabbit 表示 RabbitAnnotationDrivenConfiguration 類要生效有一個必備條件就是當前項目類路徑下要存在 org.springframework.amqp.rabbit.annotation.EnableRabbit。

我們來看看 RabbitAnnotationDrivenConfiguration 類的注解:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(EnableRabbit.class)
class RabbitAnnotationDrivenConfiguration {
}

這個類和配置文件中的內容一致。

這個預備知識搞懂了,接下來的內容就好理解了。

先來看 getConfigurationClassFilter 方法,這個就是獲取所有的過濾器,如下:

private ConfigurationClassFilter getConfigurationClassFilter() {
 if (this.configurationClassFilter == null) {
  List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
  for (AutoConfigurationImportFilter filter : filters) {
   invokeAwareMethods(filter);
  }
  this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
 }
 return this.configurationClassFilter;
}

可以看到,這里獲取到的過濾器都是 AutoConfigurationImportFilter 類型的,這個類型的過濾器只有三個實例,如下圖:

圖片圖片

從這三個實例的名字中,基本上就能看出來各自的作用:

  • OnClassCondition:這個就是條件注解 @ConditionalOnClass 的判定條件,看名字就知道用來判斷當前 classpath 下是否存在某個類。
  • OnWebApplicationCondition:這個是條件注解 ConditionalOnWebApplication 的判定條件,用來判斷當前系統環境是否是一個 Web 環境。
  • OnBeanCondition:這個是條件注解 @ConditionalOnBean 的判定條件,就是判斷當前系統下是否存在某個 Bean。

這里獲取到的三個 AutoConfigurationImportFilter 過濾器其實就是上面這三個。接下來執行 filter 方法,如下:

List<String> filter(List<String> configurations) {
 long startTime = System.nanoTime();
 String[] candidates = StringUtils.toStringArray(configurations);
 boolean skipped = false;
 for (AutoConfigurationImportFilter filter : this.filters) {
  boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
  for (int i = 0; i < match.length; i++) {
   if (!match[i]) {
    candidates[i] = null;
    skipped = true;
   }
  }
 }
 if (!skipped) {
  return configurations;
 }
 List<String> result = new ArrayList<>(candidates.length);
 for (String candidate : candidates) {
  if (candidate != null) {
   result.add(candidate);
  }
 }
 return result;
}

這里就是遍歷這三個過濾器,然后分別調用各自的 match 方法和 144 個自動化配置類進行匹配,如果這些自動化配置類所需要的條件得到滿足,則 match 數組對應的位置就為 true,否則就為 false。

然后遍歷 match 數組,將不滿足條件的自動化配置類置為 null,最后再把這些 null 移除掉。

這樣就獲取到了我們需要進行自動化配置的類了。

最后一句 fireAutoConfigurationImportEvents 則是觸發自動化配置類導入事件,這個沒啥好說的~

當這些自動化配置類加載進來之后,接下來就是各種條件注解來決定這些配置類是否生效了,這些都比較簡單了,之前在 vhr 種也和小伙伴們講過多次了,這里就不再啰嗦了~

責任編輯:武曉燕 來源: 江南一點雨
相關推薦

2025-05-07 03:33:00

2024-11-06 12:29:02

2025-01-15 08:34:00

分布式事務服務

2024-07-26 10:35:00

2024-09-04 09:43:36

2024-11-15 12:04:33

K8S容器化應用

2024-05-31 13:34:57

2024-08-29 10:23:42

2024-08-08 13:01:53

2024-07-12 08:42:58

Redis高性能架構

2024-08-07 14:56:00

Nginx反向代理配置

2024-08-12 16:09:31

2024-09-14 11:36:02

2025-01-26 11:54:39

分布式存儲系統

2024-12-31 00:00:01

驅動設計應用場景業務邏輯

2024-08-30 10:29:21

2025-08-27 06:25:00

MSTP網絡端口

2023-06-30 07:51:44

springboot初始化邏輯

2016-01-15 09:38:49

2024-08-13 15:07:20

點贊
收藏

51CTO技術棧公眾號

精品国产乱码久久久久久久| 91啪亚洲精品| 91精品国产综合久久久久久| 波多野结衣乳巨码无在线| 国产一区二区三区四区二区 | 日韩电影在线播放| 久久免费视频66| 欧美日韩一级片网站| 国产精品99久久免费黑人人妻| 视频一区日韩| 亚洲成人精品视频| 一区 二区 三区| 成人一道本在线| 5566日本婷婷色中文字幕97| aa在线视频| 在线中文字幕一区| 又黄又免费的网站| 久久99精品久久只有精品| 国产综合色香蕉精品| 国产精品一区二区免费福利视频| 色999日韩国产欧美一区二区| 日日噜噜夜夜狠狠| 国产黄人亚洲片| 日韩video| 性娇小13――14欧美| www日韩中文字幕在线看| 男女羞羞视频网站| 噜噜噜久久亚洲精品国产品小说| 成人免费自拍视频| 国产欧美日本| 久久久久久亚洲精品不卡4k岛国 | 无人在线观看的免费高清视频| 成人免费网址在线| av高清在线| 黄网av在线| 欧美精品在线一区二区三区| 午夜老司机在线观看| 欧美猛男超大videosgay| 国产不卡123| 久久久久久久97| 久久免费大视频| 日本亚洲自拍| 国产性色一区二区| 国产亚洲依依| 久久久www成人免费精品| 五月天久久久| 日韩视频一二三| 亚洲午夜一二三区视频| 里番在线播放| 琪琪亚洲精品午夜在线| 欧美超碰在线| 99热自拍偷拍| 日韩小视频在线观看专区| 日韩av综合| 欧美一级片免费观看| 国产高清精品网站| 免费观看羞羞视频网站| 国产在线视频不卡二| 丰满人妻中伦妇伦精品app| 色视频成人在线观看免| www 久久久| 黄色a级在线观看| 日韩欧美中文在线| 日韩护士脚交太爽了| 久久精品国产欧美激情| 亚洲永久视频| 日韩电影网址| 狠狠躁夜夜躁久久躁别揉| 99亚洲国产精品| 唐人社导航福利精品| 日韩精品专区在线影院观看| 天海翼精品一区二区三区| 色哺乳xxxxhd奶水米仓惠香| 欧美性xxxx极品hd满灌| 精品一区二区三区中文字幕视频 | 国产成人精品三级高清久久91| www.亚洲视频.com| 欧美性生活久久| 日本一区二区免费高清| 中文精品视频一区二区在线观看| 91福利精品视频| 中文一区一区三区高中清不卡免费| 一本色道久久88亚洲综合88| 六月丁香综合在线视频| 日本大片在线播放| 天堂av一区二区| 精品久久久久久亚洲国产300| 日本在线看片免费人成视1000| 欧美性一区二区三区| 国产精品短视频| 自拍亚洲图区| 在线视频精品一区| 一区二区在线视频播放| 成人做爰69片免费看网站| 少妇视频一区| 色噜噜狠狠色综合网| 亚洲欧美国产77777| 中文字幕一区二区三三| 影音先锋可以看的网站| 99电影网电视剧在线观看| 亚洲丝袜制服诱惑| 小视频免费在线观看| 日本高清xxxx| 亚洲欧美精品一区| 玖玖玖国产精品| 亚洲一区二区av| 在线国产91| 熟妇人妻va精品中文字幕| 日韩一级片在线观看| 国产成人一级电影| 日韩欧美中文| 一级二级在线观看| 国产精品男女猛烈高潮激情| 亚洲欧美国产高清| 看亚洲a级一级毛片| 自拍视频在线播放| 欧美成人福利在线观看| 色女人综合av| 在线成人激情黄色| 91浏览器在线视频| 日韩激情免费| 亚洲欧美小说色综合小说一区| 国产乱妇乱子| www.av91| 国产精品夫妻激情| 亚洲国产私拍精品国模在线观看| 国产午夜一区二区三区| 麻豆久久婷婷| 欧美激情极品| 天堂成人在线| 超碰在线一区二区三区| 国产av麻豆mag剧集| 97国产真实伦对白精彩视频8| 日韩欧美视频一区| 欧美日高清视频| 欧美色另类天堂2015| 岛国av在线一区| 国产精品一级二级三级| 99成人在线| 91久久亚洲| 老司机一区二区三区| 国模一区二区三区| 豆花视频一区二区| 国产一区二区三区视频在线| 电影91久久久| 色综合久久久| 美女福利一区二区| 黄色网页在线看| av资源网在线播放| 日本三级韩国三级欧美三级| 黄色小网站在线观看| 美女日批视频在线观看| 国产精品天堂| www.com黄色片| 91精品婷婷国产综合久久蝌蚪| 久久人人爽亚洲精品天堂| 日韩亚洲欧美中文高清在线| 日韩av在线不卡| 美女国内精品自产拍在线播放| 国外成人在线直播| 国产中文字幕亚洲| 成人av资源| 成人午夜免费剧场| 黄色漫画在线免费观看| 国产毛片av在线| 中文字幕日本在线观看| 欧美成人精品三级网站| h片在线免费观看| 国产精品日本一区二区三区在线| 欧美特大特白屁股xxxx| 久久中文视频| 美女任你摸久久| 国产午夜精品一区二区三区嫩草| 国内精品久久久久影院色| 国产成人8x视频一区二区| 国产精品视频看| 日韩欧美一区二区不卡| 亚洲热线99精品视频| 国产一区二区免费视频| 韩国精品久久久| 91论坛在线播放| 国产精品免费观看视频| 亚洲精品动漫久久久久| 国产精品亚洲第一区| 18禁裸男晨勃露j毛免费观看| 中文字幕av网| 伊人网在线免费观看| 中文字幕在线播放| 91福利国产在线观看菠萝蜜| 91久久精品无嫩草影院| 亚洲第一二三四区| 久久久成人av毛片免费观看| 国产一区二区三区免费观看在线| 午夜日韩福利| 97se狠狠狠综合亚洲狠狠| 精品少妇一区二区三区视频免付费| 亚洲精品资源在线| 欧美日本韩国国产| 成人在线观看一区| 国产精品久久久久久妇女|