【spring-boot 源碼解析】spring-boot 依賴管理

  • 2019 年 10 月 3 日
  • 筆記

關鍵詞:spring-boot 依賴管理、spring-boot-dependencies、spring-boot-parent

問題

maven 工程,依賴管理是非常基本又非常重要的功能,現在的工程越來越龐大,依賴越來越多,各種二方包、三方包太多太多,依賴衝突處理起來真是讓人頭疼,經常需要涉及到多個地方需要調整。

微信公眾號:逸飛兮(專註於java知識領域的源碼分析,從源碼中理解框架/工具原理、驗證CS專業知識)

解決方案

使用統一的依賴管理模組來管理工程中的所有依賴。

spring-boot 工程常使用 spring-boot-dependencies、spring-boot-starter-parent 管理工程依賴。

spring-boot 的最上級工程是 spring-boot-build,以下開始一步一步深入了解 spring-boot 依賴解決方案。

spring-boot 中的方案

spring-boot-build

spring-boot 的最上層工程,指定了 maven profiles、maven repositories、maven pluginRepositories、maven build pluginManagement。

  • profiles:中包含程式碼風格檢查、程式碼風格 format;更方便導入 eclipse;maven 倉庫
  • repositories:允許在開發過程中導入快照和里程碑 BOM。這個部分在 install/deploy 期間被 flatten 插件刪除。包含 maven 中央倉庫、spring 快照倉庫、spring 里程碑倉庫
  • pluginRepositories:插件倉庫,包含 maven 中央倉庫、spring 快照倉庫、spring 里程碑倉庫
  • pluginManagement:構建插件管理,這個插件的配置只用於存儲 Eclipse m2e 設置,它對 Maven 構建本身沒有影響。

spring-boot-dependencies

dependencies 的父工程是spring-boot-build,不包含程式碼,只用 pom 來管理依賴,pom.xml 如下:

<parent>          <groupId>org.springframework.boot</groupId>          <artifactId>spring-boot-build</artifactId>          <version>${revision}</version>          <relativePath>../..</relativePath>  </parent>  <artifactId>spring-boot-dependencies</artifactId>  <packaging>pom</packaging>  <dependencyManagement>    <!-- 省略具體依賴管理 -->  </dependencyManagement>  <build>    <pluginManagement>      <!-- 省略具體構建插件管理 -->    </pluginManagement>    <plugins>      <!-- 省略具體構建插件 -->    </plugins>  </build>

從 pom 中可以看出,spring-boot-dependencies 中除了引入了(3 個)插件,更多的是做版本的管理。

其中,引入的插件是:

  • flatten-maven-plugin:對 pom 精簡插件
  • xml-maven-plugin:1. 根據 schema 驗證 XML 文件;2. 使用 XSLT 樣式轉換 XML 文件
  • build-helper-maven-plugin:指定多個源碼目錄

dependencyManagement 中差不多管理了 spring-boot 工程中所有的依賴。

pluginManagement 中管理了常用的各種 maven 插件,這裡就不詳述了。

其中包含了 maven-clean-plugin、maven-compiler-plugin、maven-assembly-plugin、maven-war-plugin、maven-jar-plugin、spring-boot-maven-plugin,其中 spring-boot-maven-plugin 插件對於 spring-boot 工程非常重要,會把 maven 打包成的 jar 重新打包成可執行 jar。

spring-boot-starter-parent(重要)

既然有了 spring-boot-dependencies 這麼豐富的依賴、插件版本管理,那麼還搞一個 spring-boot-starter-parent 呢?

spring-boot-starter-parent 的父工程是spring-boot-dependencies,不包含程式碼,只用 pom 來管理依賴,pom.xml 如下:

<parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-dependencies</artifactId>    <version>${revision}</version>    <relativePath>../../spring-boot-dependencies</relativePath>  </parent>  <artifactId>spring-boot-starter-parent</artifactId>  <packaging>pom</packaging>  <name>Spring Boot Starter Parent</name>  <description>Parent pom providing dependency and plugin management for applications    built with Maven</description>  <properties>    <main.basedir>${basedir}/../../..</main.basedir>    <java.version>1.8</java.version>    <!-- 資源分隔符 -->    <resource.delimiter>@</resource.delimiter> <!-- delimiter that doesn't clash with Spring ${} placeholders -->    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>    <maven.compiler.source>${java.version}</maven.compiler.source>    <maven.compiler.target>${java.version}</maven.compiler.target>  </properties>  <build>    <resources>      <resource>        <directory>${basedir}/src/main/resources</directory>        <filtering>true</filtering>        <includes>          <include>**/application*.yml</include>          <include>**/application*.yaml</include>          <include>**/application*.properties</include>        </includes>      </resource>      <resource>        <directory>${basedir}/src/main/resources</directory>        <excludes>          <exclude>**/application*.yml</exclude>          <exclude>**/application*.yaml</exclude>          <exclude>**/application*.properties</exclude>        </excludes>      </resource>    </resources>    <pluginManagement>      <plugins>        <!-- 省略其它不用太關心的 plugin -->        <!-- spring-boot 提供的 maven 重打包插件,重要!!! -->        <plugin>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-maven-plugin</artifactId>              <executions>                <execution>                  <id>repackage</id>                  <goals>                    <goal>repackage</goal>                  </goals>                </execution>              </executions>              <configuration>                <mainClass>${start-class}</mainClass>              </configuration>            </plugin>      </plugins>    </pluginManagement>    <plugins>              <!-- 引入公共插件:flatten-maven-plugin、xml-maven-plugin -->    </plugins>

特性

  • 默認編譯版本:Java 1.8
  • 源碼編碼:UTF-8
  • 繼承自 spring-boot-dependencies 的 dependencyManagement
  • spring-boot-maven-plugin 的 goal 設置為 repackage
  • maven 資源過濾(application*.yml、application*.yaml、application*.properties 等)、插件配置
  • 資源分隔符:「@」,在 application*.yml 中使用@來引用 maven 屬性,常見用法如下:spring.application.name=@artifactId@

Note that, since the application.properties and application.yml files accept Spring style placeholders (${…}), the Maven filtering is changed to use @..@placeholders. (You can override that by setting a Maven property called resource.delimiter.)

譯:

注意,由於 application.properties 和 application.yml 文件接受 spring 樣式的佔位符($…),所以 maven filter 將更改為使用@…@佔位符。(可以通過設置名為 resource.delimiter 的 maven 屬性來覆蓋該屬性。)

spring-boot-parent

<parent>          <groupId>org.springframework.boot</groupId>          <artifactId>spring-boot-dependencies</artifactId>          <version>${revision}</version>          <relativePath>../spring-boot-dependencies</relativePath>  </parent>  <artifactId>spring-boot-parent</artifactId>  <packaging>pom</packaging>  <dependencyManagement>    <!-- 省略具體依賴管理 -->  </dependencyManagement>  <dependencies>    <!-- 省略具體依賴 -->  </dependencies>  <build>    <pluginManagement>      <!-- 省略具體構建插件管理 -->    </pluginManagement>    <plugins>      <!-- 省略具體構建插件 -->    </plugins>    <profiles>      <!-- 省略具體 profile -->    </profiles>  </build>

dependencyManagement

包含兩個部分:

  • 內部未發布的 spring-boot 依賴
  • 附加的 Spring 引導依賴項 (對用戶無效)

因此,這裡所加入的依賴管理,用戶不需要關心,很好,省心。

dependencies

公共的依賴,主要是一些測試依賴,如:junit、hamcrest、mockito、spring-test,還有斷言依賴:assertj。

plugins

添加了 spring-boot 公用的一些插件,如:maven-compiler-plugin、maven-jar-plugin、maven-war-plugin、maven-source-plugin 等

profiles

用戶基本不用關心。省略

選擇

spring-boot-dependencies 和 spring-boot-starter-parent、 spring-boot-parent 都提供了依賴管理的功能,那我們在開發的過程中,到底使用哪個呢?

  • spring-boot-parent :目的不是提供給用戶使用的,使用 spring-boot 開源項目用於管理 spring-boot-project 整個大工程中的除了 spring-boot-starters (提供給我們使用的各個開箱即用的三方包) 的其他模組的。
  • spring-boot-starter-parent:我們通過 Spring Initializr 構建一個 spring-boot 項目的時候,官方默認是讓我們使用的 spring-boot-starter-parent ,大致可以認為官方建議使用此方式管理依賴,畢竟此方式提供的依賴、插件管理更多,更適合使用。
  • spring-boot-dependencies:若在使用的時候,工程不想指定父工程,或者必須使用公司的父工程,可以通過 dependencyManagement 引入此依賴管理。

使用 spring-boot-dependencies,相比較 spring-boot-starter-parent 的時候特別注意要加上spring-boot-maven-plugin,如下:

<build>    <plugins>      <plugin>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-maven-plugin</artifactId>      </plugin>    </plugins>    <pluginManagement>      <plugins>        <plugin>          <groupId>org.springframework.boot</groupId>          <artifactId>spring-boot-maven-plugin</artifactId>          <version>${springboot.version}</version>        </plugin>      </plugins>    </pluginManagement>  </build>

至於 spring-boot-starter-parent 的其他額外指定的 jar,按需添加。

實際使用

在工程中使用的時候,所有的二方、三方 jar 都應該統一管理,除了 spring-boot 提供的依賴,我們還有很多 jar 需要管理,如:mysql 驅動包、mybatis 包、各種工具包或者公司內的二方包等。因此,最好使用一個單獨的模組來構建自己的 dependencies 或 parent。

待續

em……寫到這裡就結束了嗎?似乎還沒有,還需要細緻分析下一些具體依賴是如何選擇的,比如:spring-boot 選擇的是什麼日誌框架,logback?log4j2?log4j?那對於程式碼中不是用 spring-boot 指定的日誌實現時,spring-boot 又是怎麼做的呢?期待後續更新?更或者不更新,誰知道呢?

公眾號:逸飛兮(專註於java知識領域的源碼分析,從源碼中理解框架/工具原理、驗證CS專業知識的應用