Springboot 自動配置淺析

Introduction

咱們知道,SpringBoot之因此強大,就是由於他提供了各類默認的配置,可讓咱們在集成各個組件的時候從各類各樣的配置文件中解放出來。java

拿一個最普通的 web 項目舉例。咱們須要使用 servlet 容器,SpringBoot 就提供了嵌入式的 Tomcat 做爲默認容器,不須要一行配置就能直接以最普通的 Java 程序的方式啓動:java -jar;接收請求須要一個網絡端口,默認配置好8080;處理請求須要 servlet 的多線程特性,默認配置好了最大線程數爲200;處理好的請求以Restful 風格返回給調用方,SpringBoot 默認配置好了jackson進行 json 序列化,業務代碼須要作的只是返回一個 POJO 對象;鏈接池直接就默認配置了性能最好的的 Hikari,以及鏈接池的默認尺寸爲10……在一個簡單的web應用中,這些配置咱們甚至均可能不瞭解或者沒有意識到它們的存在,就可讓程序正常運行。web

這就是自動配置的魔力——潤物細無聲。redis

那麼自動配置是怎麼實現的呢?本文就從POM文件和 @SpringBootApplication 註解來簡單分析一下自動配置原理。spring

真的只是簡單地,分析一下。json

POM文件

環境

  • SpringBoot 2.0.6

父項目

在每個 SpringBoot 項目一開始的 POM 文件中,就有一個父依賴springboot

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.6.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
</parent>

點進artifactId以後來到 spring-boot-starter-parent-2.0.6.RELEASE.pom 文件,這個文件還有一個父依賴:網絡

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.0.6.RELEASE</version>
  <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

再繼續點進去來到 spring-boot-dependencies-2.0.6.RELEASE.pom 文件中。在該文件的 <properties> 標籤中能夠看到各類各樣的組件的版本信息, <dependencyManagement> 標籤中聲明瞭各個組件的maven座標信息。其中,我數了一下,在 SpringBoot 2.0.6 中一個有177個帶有版本信息的組件名,包括了你們熟悉的 elasticsearch、activemq、redis、kafka等等。多線程

這裏(spring-boot-dependencies-2.0.6.RELEASE.pom文件)稱爲 SpringBoot 的「版本仲裁中心」,它是用來管理 SpringBoot 應用裏面的全部依賴版本的地方。 它包含了大部分開發中會用到的一些組件的版本,因此咱們導入依賴默認是不須要寫版本號的,SpringBoot 會自動幫咱們配置須要的版本。這樣在引入某個組件的時候不用考慮這個組件和系統中已有的組件兼不兼容之類的問題,避免了煩人的版本衝突問題。(固然,沒有在 dependencies裏面管理的依賴天然須要聲明版本號)app

啓動器(Starters)

父項目作版本仲裁,那麼真正的 jar 包是從哪裏導入進來的呢?這就要說到咱們的starters了。點開web-starter能夠看到它幫咱們導入了web模塊正常運行所依賴的組件,就不用咱們一個一個手動導入了。elasticsearch

因此,所謂的Starters就是一系列依賴描述的組合,咱們能夠經過導入這些starters就會有相應的依賴了並能夠基於他們進行相應的開發

Springboot把開發中中會遇到的場景都抽象成一個個的starter,好比(),只須要在項目裏面引入這些starter 相關場景的全部依賴都會導入進來。要用什麼功能就導入什麼場景的啓動器

@SpringBootApplication

一個典型的 springboot 項目的入口類以下所示:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

只要運行main方法就能啓動該應用。main 中運行 run 方法時須要傳入一個類,這個類正是使用@SpringBootApplication註解標註的類,若是沒有這個註解程序是跑不起來的。

這個註解標註在某個類上就說明這個類是 springboot 的主配置類,springboot 就應該運行這個類的 main 方法來啓動springboot應用

點開這個註解的源碼,能夠看到這是一個組合註解,最主要的是這兩個註解:

  • @SpringBootConfiguration

  • @EnableAutoConfiguration

@SpringBootConfiguration標註在某個類上,表示這是一個 SpringBoot 的配置類,它的底層是@Configuration,屬於spring的底層註解,標註了這個註解的類表名這個類是一個配置類,取代之前開發中的xml文件配置,同時也代表這個類是 spring 容器中的組件,受容器管理。

接下來是@EnableAutoConfiguration註解,它的用處是開啓自動配置,使用 spring 開發須要手動配置的東西,如今由 springboot 幫咱們配置了,具體的實現就是經過這個註解來實現的。該組合註解有兩個。

1. @AutoConfigurationPackage

它的 註解中的核心代碼是 @Import({Registrar.class})

@Import 的做用就是爲容器中導入一個它指定的組件。

Registrar 這個類中有一個方法叫registerBeanDefinitions,用於註冊一些 bean 定義信息:

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
     AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}

打個斷點在這一行代碼上,運行

(new AutoConfigurationPackages.PackageImport(metadata)).getPackageName()

獲得的結果是主配置類所在的包名,目的就是將主配置類所在包及下面全部子包裏面的全部組件掃描到Spring容器中。同時也說明了,若是在主配置所在包的上層包添加組件的話是不會被掃描到的、不起做用的。

2. @Import({AutoConfigurationImportSelector.class})

該註解導入了一個自動配置的選擇器,真正地給容器中導入 springboot 自動幫咱們配置好的配置類。在 AutoConfigurationImportSelector 這個選擇器中的 getAutoConfigurationEntry 方法中,以全限定類名的方式把全部須要的配置類導入 springboot 容器中。這些全限定類型所在文件路徑爲:

org/springframework/boot/spring-boot-autoconfigure/2.1.8.RELEASE/spring-boot-autoconfigure-2.1.8.RELEASE.jar!/META-INF/spring.factories

上述兩個註解一個負責掃描咱們將要加容器中的類,一個加入 springboot 爲咱們自動配置好的類,springboot 經過這兩種方式省略了咱們大量的配置工做。

總結

本文從用於maven項目管理的pom.xml文件和標註在啓動類上的@SpringBootApplication註解,簡單分析了springboot 自動配置的實現。前者經過版本仲裁中心爲咱們維護項目組件的版本,防止依賴衝突;後者經過在加載程序的時候導入數以百計的自動配置類實現自動配置。

原文發表於:https://pengcheng.site/2019/10/28/springboot-zi-dong-pei-zhi-qian-xi/

相關文章
相關標籤/搜索