1. 程式人生 > >【SpringBoot】迭代釋出下的Jar瘦身實踐

【SpringBoot】迭代釋出下的Jar瘦身實踐

背景

隨著Spring Boot的流行,越來越多開發者選擇使用Spring Boot來發布Web應用。不同於傳統的War包釋出,Spring Boot把整個專案打包成一個可執行的Jar包(即所謂的Flat Jar),導致了這個Jar包很大(通常有40M+)。如今迭代釋出時常有的事情,每次都上傳一個如此龐大的檔案,會浪費很多時間。

下面就以一個小專案為例,簡述小弟所用的瘦身方案。當然如果是內網釋出或者你用的寬頻異常給力,瘦身就沒有多大意義了。

實踐

專案簡介

新建一個練習用的專案,其結構如下
這裡寫圖片描述

  1. ht-cdn存放的是靜態資源(如第三方js、css、images等)
  2. ht-domain專案中的資料實體定義
  3. ht-repository資料層介面及實現
  4. ht-service業務邏輯介面及實現
  5. ht-ui-webWeb管理

其中ht-ui-web依賴於ht-domainht-repositoryht-service,實現了一個簡單的GetMapping

接著打包專案,整個jar包24M這樣
這裡寫圖片描述

瘦身準備

首先我們要對Jar包有一個初步認識,它的內部結構如下

example.jar
 |
 +-META-INF
 |  +-MANIFEST.MF
 +-org
 |  +-springframework
| +-boot | +-loader | +-<spring boot loader classes> +-BOOT-INF +-classes | +-mycompany | +-project | +-YourClasses.class +-lib +-dependency1.jar +-dependency2.jar

執行該Jar時預設從BOOT-INF/classes載入class,從BOOT-INF/lib載入所依賴的Jar包。如果想要加入外部的依賴Jar,可以通過設定環境變數LOADER_PATH

來實現。

如此一來,就可以確認我們的思路了:
1. 把那些不變的依賴Jar包(比如spring依賴、資料庫Driver等,這些在不升級版本的情況下是不會更新的)從Flat Jar中抽離到單獨的目錄,如libs
2. 在啟動Jar時,設定LOADER_PATH使用上一步的libs

這樣,我們最終打包的jar包體積就大大減少,每次迭代後只需要更新這個精簡版的Jar即可。

具體步驟

打包時瘦身

通常我們是用spring-boot-maven-plugin進行打包,通過閱讀文件發現可以通過配置使得該外掛在打包時忽略特定的依賴,文件在spring-boot-maven-plugin

首先備份一下原先的依賴:編譯打包成Flat Jar,然後將BOOT-INF/lib下的除去ht-*相關的jar檔案全部解壓出來,另存到libs目錄下。

接著修改pom.xml配置如下

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <layout>ZIP</layout>
                <!--去除在生產環境中不變的依賴-->
                <excludeGroupIds>
                    org.springframework.boot,
                    org.springframework,
                    org.springframework.data,
                    org.mongodb,
                    com.github.0604hx,
                    com.fasterxml.jackson.core,
                    commons-beanutils,
                    commons-codec,
                    org.apache.commons,
                    org.apache.tomcat.embed,
                    org.hibernate,
                    org.slf4j,
                    com.jayway,
                    org.jboss,
                    com.alibaba,
                    com.fasterxml,
                    commons-collections,
                    ch.qos.logback,
                    org.scala-lang,
                    org.yaml,
                    org.jboss.logging,
                    javax.validation,
                    nz.net.ultraq.thymeleaf,
                    org.thymeleaf,
                    ognl,
                    org.unbescape,
                    org.javassist
                </excludeGroupIds>
            </configuration>
        </plugin>
    </plugins>
</build>

此時打包好的ht-ui-web.jar只有117kb這樣
這裡寫圖片描述

BOOT-INF/lib下只有ht相關的jar
這裡寫圖片描述

但是由於沒有其他依賴,ht-ui-web.jar是不能如期執行起來的

java -jar ht-ui-web-1.0.jar
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
        at org.springframework.boot.loader.PropertiesLauncher.main(PropertiesLauncher.java:521)
Caused by: java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication
        at com.nerve.huotong.web.WebApplication.main(WebApplication.java:21)
        ... 8 more
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:94)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 9 more

至此我們要設定LOADER_PATH,如下

java -Dloader.path="libs/" -jar ht-ui-web.jar

便可以看到熟悉的Spring Boot 啟動資訊了。

繼續瘦身

上面的專案結構介紹提到了ht-cdn,我是把前端用到的庫都放在這裡。然後單獨啟動一個Web Application。其他專案需要用到靜態資源就直接使用。

還有另外一個做法是,把resources/public直接丟到libs下(就是跟上一步剝離出來的jar包放在一起),結構如下:

這裡寫圖片描述

這樣也是可以的(不過要注意不能跟真實專案中自己寫的靜態資源重名)。

結語

經過上面的瘦身,每次迭代開發開的Jar包就顯得苗條多了。

插些題外話

Spring Boot 中的 banner.txt

banner是spring boot 應用啟動時列印在控制檯的彩蛋資訊,預設是這樣的


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.3.RELEASE)

想要修改這個文字的話,只需要在resources下新建banner.txt即可。這裡可以自定義banner:http://patorjk.com/software/taag