Spring Boot 項目瘦身指南,瘦到難以想象!129M->1.3M

以前在 從使用傳統Web框架到切換到Spring Boot後的總結 中提到關於 Spring Boot 編譯打包,Spring Boot 應用程序不用額外部署到外部容器中,能夠直接經過 Maven 命令將項目編譯成可執行的 jar 包,而後經過 java -jar 命令啓動便可,很是方便。html

最近有小夥伴私信我說,打 jar 包方即是方便,就是每次打包出來的 jar 太大了,先不說上傳時間的問題,若是隻修改了 1 個類就須要從新打包項目,而後從新上傳項目到服務器,怎麼以爲還不如我以前使用 war 包方便呢,使用 war 包時,雖然要部署到 Tomcat 中,但只須要將修改的 class 替換一下,重啓一下 Tomcat 就能夠了。。。java

其實到底選擇哪一種打包方式,主要仍是看我的習慣和業務場景需求,畢竟 Spring Boot 也支持打包 war 包的。web

今天的重點不是打包方式,而是解決困惑了小夥伴打包的 jar 太大的問題。面試

正常打包項目

給 Spring Boot 打包你們應該很熟了吧,只須要在 pom.xml 文件中配置 spring-boot-maven-plugin 打包插件:spring

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

而後在項目根目錄執行 mvn clean pavkage 就能夠完成打包了,以下是我本地的一個項目打包狀況:數據庫

能夠看到打包出的 jar 應用是至關的大了,如小夥伴說的同樣,若是每次修改一個 class 文件或者配置文件,就須要從新打包而後上傳服務器的話,那確實是太麻煩了,可能上傳就浪費大部分時間。。。apache

應用瘦身(分離lib和配置文件)

其實 jar 包大的緣由在於全部的依賴包所有集成在 jar 包裏面,以下是瘦身前的 jar 包內部結構:服務器

其中 classes 就是咱們項目的代碼,僅僅1.3M,而 129MB 的 lib 目錄是項目中全部的依賴(好比spinrg、Hibernate等依賴),若是咱們能把這個 lib 目錄提取出來,整個項目就會變得特別小了。說幹就幹。網絡

咱們知道 Spring Boot 的打包終究是依賴於 Maven ,因此想到更改打包信息,無非就是指定 Maven 的配置。mybatis

在 pom.xml 添加以下信息(後文解釋):

<build>
   <finalName>你想要的jar包名稱</finalName>
    <plugins>
      <!-- 一、編譯出不帶 lib 文件夾的Jar包 -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <!--表示編譯版本配置有效-->
                <fork>true</fork>
                <!--引入第三方jar包時,不添加則引入的第三方jar不會被打入jar包中-->
                <includeSystemScope>true</includeSystemScope>
                <!--排除第三方jar文件-->
                <includes>
                    <include>
                        <groupId>nothing</groupId>
                        <artifactId>nothing</artifactId>
                    </include>
                </includes>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    
    <!-- 二、完成對Java代碼的編譯,能夠指定項目源碼的jdk版本,編譯後的jdk版本,以及編碼 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
              <!-- 源代碼使用的JDK版本 --> 
                <source>${java.version}</source>
                <!-- 須要生成的目標class文件的編譯版本 -->
                <target>${java.version}</target>
                <!-- 字符集編碼 -->
                <encoding>UTF-8</encoding>
                <!-- 用來傳遞編譯器自身不包含可是卻支持的參數選項 -->  
                <compilerArguments>
                    <verbose/>
                    <!-- windwos環境(二選一) -->
                    <bootclasspath>${java.home}/lib/rt.jar:${java.home}/lib/jce.jar</bootclasspath>
                    <!-- Linux環境(二選一) -->
                    <bootclasspath>${java.home}/lib/rt.jar:${java.home}/lib/jce.jar</bootclasspath>
                </compilerArguments>
            </configuration>
        </plugin>

        <!-- 三、將全部依賴的jar文件複製到target/lib目錄 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>prepare-package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                      <!--複製到哪一個路徑,${project.build.directory} 缺醒爲 target,其餘內置參數見下面解釋-->
                        <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        <overWriteReleases>false</overWriteReleases>
                        <overWriteSnapshots>false</overWriteSnapshots>
                        <overWriteIfNewer>true</overWriteIfNewer>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        
    <!-- 四、指定啓動類,指定配置文件,將依賴打成外部jar包 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
                <archive>
                    <manifest>
                        <!-- 是否要把第三方jar加入到類構建路徑 -->
                        <addClasspath>true</addClasspath>
                        <!-- 外部依賴jar包的最終位置 -->
                        <classpathPrefix>lib/</classpathPrefix>
                        <!-- 項目啓動類 -->
                        <mainClass>com.javam4.MyApplication</mainClass>
                    </manifest>
                </archive>
                <!--資源文件不打進jar包中,作到配置跟項目分離的效果-->
                <excludes>
                    <!-- 業務jar中過濾application.properties/yml文件,在jar包外控制 -->
                    <exclude>*.properties</exclude>
                    <exclude>*.xml</exclude>
                    <exclude>*.yml</exclude>
                </excludes>
            </configuration>
        </plugin>

    </plugins>
</build>

以下一一細拆如上配置:

一、spring-boot-maven-plugin

Springboot 默認使用 spring-boot-maven-plugin 來打包,這個插件會將項目全部的依賴打入項目 jar 包裏面,正常打包時 spring-boot-maven-plugin 結構以下:

<plugin>  
    <groupId>org.springframework.boot</groupId>   
    <artifactId>spring-boot-maven-plugin</artifactId>  
    <configuration>  
        <mainClass>com.javam4.MyApplication</mainClass>  
        <layout>ZIP</layout>  
    </configuration>  
    <executions>  
    <execution>  
         <goals>  
             <goal>repackage</goal>  
         </goals>  
     </execution>  
   </executions>  
</plugin>  

以下是提取的修改項:

<configuration>
    <!--表示編譯版本配置有效-->
    <fork>true</fork>
    <!--引入第三方jar包時,不添加則引入的第三方jar不會被打入jar包中-->
    <includeSystemScope>true</includeSystemScope>
    <!--排除第三方jar文件-->
    <includes>
        <include>
            <groupId>nothing</groupId>
            <artifactId>nothing</artifactId>
        </include>
    </includes>
</configuration>

修改的做用:

  • includeSystemScope:jar包分兩種,一種是spring、mybatis等這種項目依賴的,再就是咱們外部手動引入的第三方 jar 依賴,若是該參數不設置爲 true 的話是不能被打包進來的~
  • includes:這個節點就是排除項目中全部的 jar,那還怎麼打包?

其實咱們須要將打包插件替換爲 maven-jar-plugin,而後使用該插件拷貝依賴到 jar 到外面的 lib 目錄。

二、maven-xxx-plugin

從 二、三、4 你會發現用到了 maven-xxx-plugin 格式的三種插件,簡單說一下這三者的做用:

  • maven-compiler-plugin:

    完成對Java代碼的編譯,能夠指定項目源碼的jdk版本、編譯後的jdk版本,以及編碼,若是不寫這個插件也是沒問題的,不寫會使用默認的 jdk 版原本處理,只是這樣容易出現版本不匹配的問題,好比本地maven環境用的3.3.9版本,默認會使用jdk1.5進行編譯,而項目中用的jdk1.8的編譯環境,那就會致使打包時編譯不經過。

  • maven-dependency-plugin:

    做用就是將全部依賴的jar文件複製到指定目錄下,其中涉及到的 ${project.xx} 見下文補充。

  • maven-jar-plugin:

    主要做用就是將maven工程打包成jar包。

主要說一下 maven-jar-plugin 插件的以下配置:

<configuration>
    <!--資源文件不打進jar包中,作到配置跟項目分離的效果-->
    <excludes>
        <!-- 業務jar中過濾application.properties/yml文件,在jar包外控制 -->
        <exclude>*.properties</exclude>
        <exclude>*.xml</exclude>
        <exclude>*.yml</exclude>
    </excludes>
</configuration>

打包時排除資源配置文件,若是排除了配置文件那麼項目啓動是怎麼讀取呢?

配置文件有這麼一個默認的優先級:

當前項目config目錄下 > 當前項目根目錄下 > 類路徑config目錄下 > 類路徑根目錄下

所以只須要將配置文件複製一份到與 jar 包平級目錄下,或者與jar包平行config目錄下,就能優先使用此配置文件,達到了僞分離目的。

最終的目錄結構以下:

Maven 中的內置變量說明:

  • ${basedir} 項目根目錄
  • ${project.build.directory} 構建目錄,缺省爲target
  • ${project.build.outputDirectory} 構建過程輸出目錄,缺省爲target/classes
  • ${project.build.finalName} 產出物名稱,缺省爲{project.artifactId}-${project.version}
  • ${project.packaging} 打包類型,缺省爲jar
  • ${project.packaging} 打包類型,缺省爲jar
  • ${project.xxx} 當前pom文件的任意節點的內容

瘦身總結

Spring Boot 框架提供了一套本身的打包機制 — spring-boot-maven-plugin,Springboot 默認使用該插件來打包,打包時會將項目全部的依賴打入項目 jar 包裏面,若是咱們想要抽離依賴的 jar 僅僅使用該插件是不行的,就須要將打包插件替換爲 maven-jar-plugin,並拷貝全部的依賴到 jar 外面的 lib 目錄。

項目打包時,在分離依賴 jar 包基礎上,咱們又排除了配置文件,由於配置文件有一個默認的讀取路徑:

當前項目config目錄下 > 當前項目根目錄下 > 類路徑config目錄下 > 類路徑根目錄下

咱們只須要在當前項目 jar 包同級目錄建立一個 config 文件夾,而後將配置文件複製一份,這樣就達到了僞分離目的。

以後再修改配置文件,好比端口號、數據庫鏈接信息等,就不須要從新打包項目了,直接修改完配置文件重啓項目就能夠了。

而通過分離依賴後的 jar 包從原來的100多兆到如今的1兆,若是後面須要變動業務邏輯,只須要輕量的編譯項目,快速的實現項目的上傳替換,有效的減小了網絡開銷,提升項目部署的效率。

博客地址:https://niceyoo.cnblogs.com

更多原創內容能夠移步個人公衆號,回覆「面試」獲取我整理的2020面經。

相關文章
相關標籤/搜索