【Maven】多模塊項目打包最佳實踐

背景

一個多模塊項目,一級子模塊就有7個,一部分模塊還有自己的子模塊,包含了純pom,jar的各種模塊。
主要的子模塊是 SpringBoot項目, 需要打包成一個可執行jar。

目標

WHAT 主要:使得項目易於構建部署,適應不同場景下的複雜工程的組織、發行、交付、落地中的問題。

HOW 方法:學習大型開源項目的打包方式(發行、組織),以及學習多種打包插件。

WHERE 應用場景實踐:多場景部署,優化部署流程,減少不必要的構建和文件複製傳輸。

從現象尋找優化點

開發、測試環境中,經常改動的只有個別幾個模塊,有的時候甚至只有一個,需要一種能實現類似於增量編譯、熱部署的方式,加快流程速度。

已知有spring-boot-devtools可以在本地IDE開發環境中做到熱部署,而實際上有的項目在本地無法完整啓動,例如運維使用了防火牆限制了ZooKeeper的連接,數據庫的連接。

比較影響開發體驗。

於是把變化的和不變化的分開看待,分別打包部署,會靈活方便許多。

結構分析

封裝格式

很多開源軟件目錄結構都是類似的,bin,lib,conf,plugin等等,這個可以通過maven-assembly-plugin實現,那麼打包出來的是一個zip。

依賴

有三類,一種是其他模塊編譯出來的,通常會打包出 xxxx.jar,通過 dependency 像第三方依賴一樣引入。
第二類是私服裏面,其他項目,或者其他團隊發行的 xxxx.jar。
第三類就是中央倉庫的,第三方jar包。

分門別類

爲了便於描述,假設該項目名字叫 slankka-application

依賴類型 壓縮前的位置 期望的解壓後路徑
項目的腳本等 bin /bin
啓動類所在模塊 (slankka-server) slankka-server.jar /
啓動類所在模塊(slankka-server)的資源、配置文件,例如application.yml slankka-server/src/main/resources /conf
其他子模塊,例如 dao,common,core slankka-dao/target/slankka-dao.jar,slankka-common/target/slankka-common.jar,slankka-core/target/slankka-core.jar slankka-server.jar/
內部私服的依賴,例如其他項目的,slankka-rpc-provider .m2 slankka-server.jar/META-INF/lib
SpringBoot配置文件 slankka-server.conf /

依賴組織方式

如果只用 springboot-maven-plugin 打包,是比較常見的做法,這種方式打出來的jar 一般很大,當項目足夠複雜,可能打出來的幾百兆以上。

如果只用maven-jar-plugin,或者maven-dependency-plugin,則缺少了spring-boot-maven-plugin的優點。

解決辦法是多種插件結合使用,在不同階段使用不同的插件。

插件功能一覽

插件 階段 作用 組織範圍
maven-jar-plugin package 將資源和classes組合成jar include,exclude到jar的文件,例如不需要的exclude,例如conf,logging
maven-shade-plugin package 可以relocating類解決衝突,也可以將一些classes組裝到一個jar內 這裏可以把子模塊的jar 裝進springboot的jar
spring-boot-maven-plugin package:repackage 將資源按照Spring自己的方式打包,設置layout等 打成可執行文件
maven-dependency-plugin package:copy-dependencies 將scope=compile的jar複製到lib 複製所有依賴到lib
maven-assembly-plugin package:single 最後一步,打包成一個ZIP包 將bin,conf,lib 等打包成一個ZIP

應用細節

對於開發環境,maven-dependency-plugin,maven-assembly-plugin 構建過程,可以用 profile 做成可選:

例: 僅linux服務器需要打包成ZIP,Windows, Mac電腦不需要。

<activation>
        <activeByDefault>false</activeByDefault>
        <os>
          <name>linux</name>
          <family>unix</family>
          <arch>amd64</arch>
        </os>
</activation>

只有第一次打包上傳到服務器時,才需要一個完整的ZIP。

快速迭代

對於日常開發,敏捷開發等,只需要前面三個插件。

(實踐: 將原本200MB的jar (fat-jar或者ZIP)縮減成2MB左右。這樣直接發佈到服務器上,很快。)

3rd-party 和 子模塊的依賴分開

對於子模塊的迭代,單獨打包(假如不用shade方式和springboot的jar放在一起),則會被maven-dependency-plugin複製到lib,和第三方jar混在一起,有時候會忘記替換,造成失誤,不如按照半導體芯片領域模塊化思維,直接集成到一個jar,那麼就使用shade 把子模塊的artifact壓縮到一起,這樣的好處就是,lib下,完全可以稱之爲 3rdParty,這部分幾乎不會有變動。即便有的話,來一次完整的構建即可。

推薦使用shade插件將其他子模塊和應用程序啓動模塊作爲整體

shade的好處是,將子模塊的字節碼classes 和springboot應用所在模塊放在同樣的位置。
相比而言,spring-boot-maven-plugin 會放進 META-INF/lib內,這個非常適合其他項目的lib,作爲整體嵌入這個jar。

弄清楚這些目標以後,就知道插件的include和exclude怎麼寫了。

插件負責的範圍

插件 依賴類型 配置方法
spring-boot-maven-plugin 其他項目的依賴 configuration.includes
maven-dependency-plugin 第三方依賴 configuration.excludeArtifactIds 排除子模塊、其他私服中的項目artifact
maven-shade-plugin 本項目的子模塊 configuration.artifactSet.include

附錄

以下配置,均在 slankka-server/pom.xml 內,最外層 slankka-application/pom.xml 是一個pom。

根據情況,有些放在build內,有些可放在 profile.build內。

maven-shade-plugin 的配置

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration combine.children="append">
        <createDependencyReducedPom>false</createDependencyReducedPom>
        <artifactSet>
          <includes>
            <include>com.slankka.cloud:slankka-api</include>
            <include>com.slankka.cloud:slankka-cli</include>
            <include>com.slankka.cloud:slankka-common</include>
            <include>com.slankka.cloud:slankka-dao</include>
            <include>com.slankka.cloud:slankka-api</include>
          </includes>
        </artifactSet>
      </configuration>
    </execution>
  </executions>
</plugin>

spring-boot-maven-plugin的打包配置

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <version>${springboot.maven.plugin.version}</version>
  <executions>
    <execution>
      <goals>
        <goal>repackage</goal>
      </goals>
      <configuration>
        <attach>false</attach>
      </configuration>
    </execution>
  </executions>
  <configuration>
    <mainClass>com.slankka.server.Application</mainClass>
    <layout>ZIP</layout> <!--不記得爲什麼這麼配,但壓縮包ZIP是另外的插件實現-->
    <executable>true</executable> <!--非常好用,會注入bin/bash腳本到jar的頭部,可以當shell腳本用-->
    <includes>
      <include>
        <groupId>com.slankka.rpc</groupId>
        <artifactId>rpc-provider</artifactId>
      </include>
      <include>
        <groupId>com.slankka.cloud</groupId>
        <artifactId>mikasa-cloud-api</artifactId>
      </include>
    </includes>
  </configuration>
</plugin>

如果沒有要攜帶的其他項目依賴,則直接include裏面寫 none:none


maven-dependency-plugin的打包配置

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <version>${maven-dependency-plugin.version}</version>
  <executions>
    <execution>
      <id>copy-dependencies</id>
      <phase>package</phase>
      <goals>
        <goal>copy-dependencies</goal>
      </goals>
      <configuration>
        <excludeArtifactIds>
          slankka-api,slankka-cli,slankka-common,slankka-dao,slankka-rpc-provider,mikasa-cloud-api
        </excludeArtifactIds>
        <outputDirectory>target/lib</outputDirectory>
        <excludeTransitive>false</excludeTransitive>
        <stripVersion>false</stripVersion>
        <includeScope>runtime</includeScope>
      </configuration>
    </execution>
  </executions>
</plugin>

maven-assembly-plugin的配置

maven-jar-plugin 的配置

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章