SpringBoot-Maven打包壓縮瘦身方式
一、Spring Boot 可執(zhí)行 jar 分析
Spring Boot 中默認打包成的 jar 叫做 可執(zhí)行 jar,這種 jar 不同于普通的 jar,普通的 jar 不可以通過 java -jar xxx.jar 命令執(zhí)行,普通的 jar 主要是被其他應用依賴,Spring Boot 打成的 jar 可以執(zhí)行,但是不可以被其他的應用所依賴,即使強制依賴,也無法獲取里邊的類。
但是可執(zhí)行 jar 并不是 Spring Boot 獨有的,Java 工程本身就可以打包成可執(zhí)行 jar 。
Spring Boot 項目中一個默認的插件配置 spring-boot-maven-plugin ,這個打包插件存在 5 個方面的功能,從插件命令就可以看出:
五個功能分別是:
build-info
:生成項目的構建信息文件 build-info.propertiesrepackage
:這個是默認 goal,在 mvn package 執(zhí)行之后,這個命令再次打包生成可執(zhí)行的 jar,同時將 mvn package 生成的 jar 重命名為 *.originrun
:這個可以用來運行 Spring Boot 應用start
:這個在 mvn integration-test 階段,進行 Spring Boot 應用生命周期的管理stop
:這個在 mvn integration-test 階段,進行 Spring Boot 應用生命周期的管理
這里功能,默認情況下使用就是 repackage 功能,其他功能要使用,則需要開發(fā)者顯式配置。
1.1 打包
repackage 功能的 作用,就是在打包的時候,多做一點額外的事情:
- 1.首先 mvn package 命令 對項目進行打包,打成一個 jar,這個 jar 就是一個普通的 jar,可以被其他項目依賴,但是不可以被執(zhí)行。
- 2.repackage 命令,對第一步 打包成的 jar 進行再次打包,將之打成一個 可執(zhí)行 jar ,通過將第一步打成的 jar 重命名為 *.original 文件
舉個例子:
對任意一個 Spring Boot 項目進行打包,可以執(zhí)行 mvn package 命令,也可以直接在 IDEA 中點擊 package ,如下 :
打包成功之后, target 中的文件如下:
這里有兩個文件,第一個 restful-0.0.1-SNAPSHOT.jar 表示打包成的可執(zhí)行 jar ,第二個 restful-0.0.1-SNAPSHOT.jar.original 則是在打包過程中 ,被重命名的 jar,這是一個不可執(zhí)行 jar,但是可以被其他項目依賴的 jar。
1.2 兩種 jar 的比較
可執(zhí)行 jar 解壓之后,目錄如下:
可執(zhí)行 jar 中,我們自己的代碼是存在 于 BOOT-INF/classes/ 目錄下,另外,還有一個 META-INF 的目錄,該目錄下有一個 MANIFEST.MF 文件,打開該文件,內容如下:
Manifest-Version: 1.0 Implementation-Title: restful Implementation-Version: 0.0.1-SNAPSHOT Start-Class: org.javaboy.restful.RestfulApplication Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Build-Jdk-Spec: 1.8 Spring-Boot-Version: 2.1.6.RELEASE Created-By: Maven Archiver 3.4.0 Main-Class: org.springframework.boot.loader.JarLauncher
可以看到,這里定義了一個 Start-Class,這就是可執(zhí)行 jar 的入口類,Spring-Boot-Classes 表示我們自己代碼編譯后的位置,Spring-Boot-Lib 則表示項目依賴的 jar 的位置。
換句話說,如果自己要打一個可執(zhí)行 jar 包的話,除了添加相關依賴之外,還需要配置 META-INF/MANIFEST.MF 文件。
這是可執(zhí)行 jar 的結構,那么不可執(zhí)行 jar 的結構呢?
首先將默認的后綴 .original 除去,然后給文件重命名,重命名完成,進行解壓:
解壓后可以看到,不可執(zhí)行 jar 根目錄就相當于我們的 classpath,解壓之后,直接就能看到我們的代碼,它也有 META-INF/MANIFEST.MF 文件,但是文件中沒有定義啟動類等。
Manifest-Version: 1.0 Implementation-Title: restful Implementation-Version: 0.0.1-SNAPSHOT Build-Jdk-Spec: 1.8 Created-By: Maven Archiver 3.4.0
注意
不可以執(zhí)行 jar 也沒有將項目的依賴打包進來。
從這里我們就可以看出,兩個 jar ,雖然都是 jar 包,但是內部結構是完全不同的,因此一個可以直接執(zhí)行,另一個則可以被其他項目依賴。
1.3 一次打包兩個 jar
一般來說,Spring Boot 直接打包成可執(zhí)行 jar 就可以了,不建議將 Spring Boot 作為普通的 jar 被其他的項目所依賴。
如果有這種需求,建議將被依賴的部分,單獨抽出來做一個普通的 Maven 項目,然后在 Spring Boot 中引用這個 Maven 項目。
如果非要將 Spring Boot 打包成一個普通 jar 被其他項目依賴,技術上來說,也是可以的,給 spring-boot-maven-plugin 插件添加如下配置:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <classifier>exec</classifier> </configuration> </plugin> </plugins> </build>
配置的 classifier 表示可執(zhí)行 jar 的名字,配置了這個之后,在插件執(zhí)行 repackage 命令時,就不會給 mvn package 所打成的 jar 重命名了,所以,打包后的 jar 如下:
第一個 jar 表示可以被其他項目依賴的 jar ,第二個 jar 則表示一個可執(zhí)行 jar。
二、SpringBoot迭代發(fā)布JAR瘦身配置
默認情況下,插件 spring-boot-maven-plugin 會把整個項目打包成一個可運行的Jar包(即所謂的Flat Jar),導致了這個Jar包很大(通常有幾十M+)。
把第三方的JAR與項目代碼分離,第三方的JAR把移除到lib文件夾中,即可實現(xiàn)為我們的可執(zhí)行JAR瘦身,配置如下:
<plugins> <!-- spring boot thin jar configuration --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <!-- main 入口 --> <mainClass>com.bdfint.logistics.app.driver.LogisticsAppDriverApplication</mainClass> <!-- 設置為ZIP,此模式下spring-boot-maven-plugin會將Manifest.MF文件中的Main-Class設置為org.springframework.boot.loader.PropertiesLauncher --> <layout>ZIP</layout> <!-- 需要包含的jar包 --> <includes> <!-- 不包含任何jar包 --> <!--<include>--> <!--<groupId>nothing</groupId>--> <!--<artifactId>nothing</artifactId>--> <!--</include>--> <include> <groupId>com.bdfint.logistics</groupId> <artifactId>logistics-api</artifactId> </include> <include> <groupId>com.bdfint.logistics</groupId> <artifactId>logistics-common</artifactId> </include> </includes> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <!-- third-party jar into lib directory --> <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> <outputDirectory>${project.build.directory}/lib</outputDirectory> <!-- 需要排除的jar的 groupId --> <excludeGroupIds> com.bdfint.logistics </excludeGroupIds> </configuration> </execution> </executions> </plugin> </plugins>
接下來,執(zhí)行打包命令:mvn clean package -Dmaven.test.skip=true,打包后在target目錄下就包含我們的JAR和lib目錄,如下圖:
CMD定位到target目錄下,執(zhí)行命令:java -Dloader.path=./lib -jar logistics-app-driver-2.9.1.1.jar,即可把項目JAR跑起來!
三、引入內部編譯的依賴
如果想引入內部編譯的依賴包可以通過includes屬性進行添加,利用引用了內部的dubbo可以通過如下配置:
配置完成后,再次執(zhí)行編譯:mvn clean package。
如果引入內部包過多,也可以通過excludeGroupIds屬性去掉不變的依賴包。
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <layout>ZIP</layout> <!--去除在生產環(huán)境中不變的依賴--> <excludeGroupIds> org.springframework.boot, org.springframework, org.springframework.data, org.apache.tomcat.embed </excludeGroupIds> </configuration> </plugin> </plugins> </build>
注:layout 必須是 ZIP 、 excludeGroupIds 中時忽略也是就需要打在外部的 jar。
3.1 剔除不需要的依賴
3.1.1 方式一:排除一個具體的maven 模塊,通過唯一的groupId和artifactId組合來實現(xiàn)。(如果有必要,可以加入classifier來唯一確認。)
<project> ... <build> ... <plugins> ... <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>1.5.6.RELEASE</version> <configuration> <excludes> <exclude> <groupId>com.foo</groupId> <artifactId>bar</artifactId> </exclude> </excludes> </configuration> ... </plugin> ... </plugins> ... </build> ... </project>
3.1.2 方式二:排除和“指定的artifactId”相符的所有maven模塊
<project> ... <build> ... <plugins> ... <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>1.5.6.RELEASE</version> <configuration> <excludeArtifactIds>my-lib,another-lib</excludeArtifactIds> </configuration> ... </plugin> ... </plugins> ... </build> ... </project>
3.1.3 方式三:排除屬于“指定的groupId”的所有maven模塊
<project> ... <build> ... <plugins> ... <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>1.5.6.RELEASE</version> <configuration> <excludeGroupIds>com.foo</excludeGroupIds> </configuration> ... </plugin> ... </plugins> ... </build> ... </project>
四、Spring Boot Thin Launcher
使用插件以后,原來打包45M的jar包,現(xiàn)在體積只有 40K,瘦身效果非常明顯,并且也不改變原有的打包方式和部署方法。打包以后的jar也是可執(zhí)行的。
第一次執(zhí)行時,會自動下載所有的依賴包,并且緩存到本地,默認的緩存路徑是${user.home}/.m2/,你也可以使用如下命令在啟動時修改依賴的路徑
java -Dthin.root=. -jar app-0.0.1-SNAPSHOT.jar
在pom.xml添加以下插件代碼,然后正常地打包即可:
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot.experimental</groupId> <artifactId>spring-boot-thin-layout</artifactId> <version>1.0.3.RELEASE</version> </dependency> </dependencies> <configuration> <executable>true</executable> </configuration> </plugin> </plugins> </build>
五、 maven的assembly打包插件
assembly配置:
在項目中創(chuàng)建一個文件,放在src/main/assembly/assembly.xml中。
assembly中的具體配置:
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> <!-- 必須寫,否則打包時會有 assembly ID must be present and non-empty 錯誤 這個名字最終會追加到打包的名字的末尾,如項目的名字為 speed-api-0.0.1-SNAPSHOT, 則最終生成的包名為 speed-api-0.0.1-SNAPSHOT-bin.zip --> <id>bin</id> <!-- 打包后的文件格式,可以是zip,tar,tar.gz,tar.bz2,jar,war,dir --> <formats> <format>zip</format> </formats> <!-- 壓縮包下是否生成和項目名相同的根目錄 --> <includeBaseDirectory>false</includeBaseDirectory> <dependencySets> <dependencySet> <!-- 不使用項目的artifact,第三方jar不要解壓,打包進zip文件的lib目錄 --> <useProjectArtifact>false</useProjectArtifact> <outputDirectory>lib</outputDirectory> <unpack>false</unpack> </dependencySet> </dependencySets> <fileSets> <!-- 把項目相關的說明文件,打包進zip文件的根目錄 --> <fileSet> <directory>${project.basedir}</directory> <outputDirectory></outputDirectory> <includes> <include>README*</include> <include>LICENSE*</include> <include>NOTICE*</include> </includes> </fileSet> <!-- 把項目的配置文件,打包進zip文件的config目錄 --> <fileSet> <directory>${project.basedir}/src/main/resources</directory> <outputDirectory>config</outputDirectory> </fileSet> <!-- 把項目的腳本文件,打包進zip文件的bin目錄 --> <fileSet> <directory>${project.basedir}/src/main/bin</directory> <outputDirectory>bin</outputDirectory> </fileSet> <!-- 把項目自己編譯出來的jar文件,打包進zip文件的根目錄 --> <fileSet> <directory>${project.build.directory}</directory> <outputDirectory></outputDirectory> <includes> <include>*.jar</include> </includes> </fileSet> </fileSets> </assembly>
maven中的配置:
<build> <plugins> <!-- 指定啟動類,將依賴打成外部jar包 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <!-- 生成的jar中,不要包含pom.xml和pom.properties這兩個文件 --> <addMavenDescriptor>false</addMavenDescriptor> <manifest> <!-- 是否要把第三方jar放到manifest的classpath中 --> <addClasspath>true</addClasspath> <!-- 外部依賴jar包的最終位置 --> <classpathPrefix>lib/</classpathPrefix> <!-- 項目啟動類 --> <mainClass>com.zbrx.speed.App</mainClass> </manifest> </archive> </configuration> </plugin> <!-- 使用assembly打包 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptors> <!-- assembly配置文件位置 --> <descriptor>src/main/assembly/assembly.xml</descriptor> </descriptors> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> <!-- 打包發(fā)布時,跳過單元測試 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <skipTests>true</skipTests> </configuration> </plugin> </plugins> </build>
最終打包后的效果:
壓縮包里的文件內容:
config配置文件:
六、SpringBoot剔除靜態(tài)文件
靜態(tài)文件排除到項目外面,pom.xml 繼續(xù)引入:
<resources> <resource> <filtering>true</filtering> <directory>src/main/resources</directory> <excludes> <exclude>static/**</exclude> </excludes> </resource> </resources>
配置nginx,訪問靜態(tài)資源文件:
server { listen 80; server_name www.cloudbed.vip; location / { proxy_pass http://127.0.0.1:8080; } #靜態(tài)文件交給nginx處理 location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|ioc|rar| zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)$ { root /cloudbed/static; expires 30d; } location ~ .*\.(js|css)?$ { root /cloudbed/static; expires 1h; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } }
6.1 打包時排除resources下的資源文件
<build> ... <resources> <resource> <directory>src/main/resources</directory> <excludes> <exclude>**/*.properties</exclude> <exclude>**/*.xml</exclude> <exclude>**/*.yml</exclude> </excludes> </resource> </resources> <plugins> ... <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> ... </plugins> ... </build>
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
springboot如何獲取application.yml里值的方法
這篇文章主要介紹了springboot如何獲取application.yml里的值,文章圍繞主題相關自資料展開詳細的內容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-04-04Java實現(xiàn)中文算數(shù)驗證碼的實現(xiàn)示例(算數(shù)運算+-*/)
這篇文章主要介紹了Java實現(xiàn)中文算數(shù)驗證碼的實現(xiàn)示例(算數(shù)運算+-*/),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07Java通過調用C/C++實現(xiàn)的DLL動態(tài)庫——JNI的方法
這篇文章主要介紹了Java通過調用C/C++實現(xiàn)的DLL動態(tài)庫——JNI的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2018-01-01springmvc—handlermapping三種映射方式
這篇文章主要介紹了springmvc—handlermapping三種映射方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09