SpringBoot-Maven打包壓縮瘦身方式
一、Spring Boot 可執(zhí)行 jar 分析
Spring Boot 中默認(rèn)打包成的 jar 叫做 可執(zhí)行 jar,這種 jar 不同于普通的 jar,普通的 jar 不可以通過 java -jar xxx.jar 命令執(zhí)行,普通的 jar 主要是被其他應(yīng)用依賴,Spring Boot 打成的 jar 可以執(zhí)行,但是不可以被其他的應(yīng)用所依賴,即使強(qiáng)制依賴,也無法獲取里邊的類。
但是可執(zhí)行 jar 并不是 Spring Boot 獨(dú)有的,Java 工程本身就可以打包成可執(zhí)行 jar 。
Spring Boot 項(xiàng)目中一個(gè)默認(rèn)的插件配置 spring-boot-maven-plugin ,這個(gè)打包插件存在 5 個(gè)方面的功能,從插件命令就可以看出:
五個(gè)功能分別是:
build-info
:生成項(xiàng)目的構(gòu)建信息文件 build-info.propertiesrepackage
:這個(gè)是默認(rèn) goal,在 mvn package 執(zhí)行之后,這個(gè)命令再次打包生成可執(zhí)行的 jar,同時(shí)將 mvn package 生成的 jar 重命名為 *.originrun
:這個(gè)可以用來運(yùn)行 Spring Boot 應(yīng)用start
:這個(gè)在 mvn integration-test 階段,進(jìn)行 Spring Boot 應(yīng)用生命周期的管理stop
:這個(gè)在 mvn integration-test 階段,進(jìn)行 Spring Boot 應(yīng)用生命周期的管理
這里功能,默認(rèn)情況下使用就是 repackage 功能,其他功能要使用,則需要開發(fā)者顯式配置。
1.1 打包
repackage 功能的 作用,就是在打包的時(shí)候,多做一點(diǎn)額外的事情:
- 1.首先 mvn package 命令 對項(xiàng)目進(jìn)行打包,打成一個(gè) jar,這個(gè) jar 就是一個(gè)普通的 jar,可以被其他項(xiàng)目依賴,但是不可以被執(zhí)行。
- 2.repackage 命令,對第一步 打包成的 jar 進(jìn)行再次打包,將之打成一個(gè) 可執(zhí)行 jar ,通過將第一步打成的 jar 重命名為 *.original 文件
舉個(gè)例子:
對任意一個(gè) Spring Boot 項(xiàng)目進(jìn)行打包,可以執(zhí)行 mvn package 命令,也可以直接在 IDEA 中點(diǎn)擊 package ,如下 :
打包成功之后, target 中的文件如下:
這里有兩個(gè)文件,第一個(gè) restful-0.0.1-SNAPSHOT.jar 表示打包成的可執(zhí)行 jar ,第二個(gè) restful-0.0.1-SNAPSHOT.jar.original 則是在打包過程中 ,被重命名的 jar,這是一個(gè)不可執(zhí)行 jar,但是可以被其他項(xiàng)目依賴的 jar。
1.2 兩種 jar 的比較
可執(zhí)行 jar 解壓之后,目錄如下:
可執(zhí)行 jar 中,我們自己的代碼是存在 于 BOOT-INF/classes/ 目錄下,另外,還有一個(gè) META-INF 的目錄,該目錄下有一個(gè) MANIFEST.MF 文件,打開該文件,內(nèi)容如下:
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
可以看到,這里定義了一個(gè) Start-Class,這就是可執(zhí)行 jar 的入口類,Spring-Boot-Classes 表示我們自己代碼編譯后的位置,Spring-Boot-Lib 則表示項(xiàng)目依賴的 jar 的位置。
換句話說,如果自己要打一個(gè)可執(zhí)行 jar 包的話,除了添加相關(guān)依賴之外,還需要配置 META-INF/MANIFEST.MF 文件。
這是可執(zhí)行 jar 的結(jié)構(gòu),那么不可執(zhí)行 jar 的結(jié)構(gòu)呢?
首先將默認(rèn)的后綴 .original 除去,然后給文件重命名,重命名完成,進(jìn)行解壓:
解壓后可以看到,不可執(zhí)行 jar 根目錄就相當(dāng)于我們的 classpath,解壓之后,直接就能看到我們的代碼,它也有 META-INF/MANIFEST.MF 文件,但是文件中沒有定義啟動(dòng)類等。
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 也沒有將項(xiàng)目的依賴打包進(jìn)來。
從這里我們就可以看出,兩個(gè) jar ,雖然都是 jar 包,但是內(nèi)部結(jié)構(gòu)是完全不同的,因此一個(gè)可以直接執(zhí)行,另一個(gè)則可以被其他項(xiàng)目依賴。
1.3 一次打包兩個(gè) jar
一般來說,Spring Boot 直接打包成可執(zhí)行 jar 就可以了,不建議將 Spring Boot 作為普通的 jar 被其他的項(xiàng)目所依賴。
如果有這種需求,建議將被依賴的部分,單獨(dú)抽出來做一個(gè)普通的 Maven 項(xiàng)目,然后在 Spring Boot 中引用這個(gè) Maven 項(xiàng)目。
如果非要將 Spring Boot 打包成一個(gè)普通 jar 被其他項(xiàng)目依賴,技術(shù)上來說,也是可以的,給 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 的名字,配置了這個(gè)之后,在插件執(zhí)行 repackage 命令時(shí),就不會(huì)給 mvn package 所打成的 jar 重命名了,所以,打包后的 jar 如下:
第一個(gè) jar 表示可以被其他項(xiàng)目依賴的 jar ,第二個(gè) jar 則表示一個(gè)可執(zhí)行 jar。
二、SpringBoot迭代發(fā)布JAR瘦身配置
默認(rèn)情況下,插件 spring-boot-maven-plugin 會(huì)把整個(gè)項(xiàng)目打包成一個(gè)可運(yùn)行的Jar包(即所謂的Flat Jar),導(dǎo)致了這個(gè)Jar包很大(通常有幾十M+)。
把第三方的JAR與項(xiàng)目代碼分離,第三方的JAR把移除到lib文件夾中,即可實(shí)現(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> <!-- 設(shè)置為ZIP,此模式下spring-boot-maven-plugin會(huì)將Manifest.MF文件中的Main-Class設(shè)置為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,即可把項(xiàng)目JAR跑起來!
三、引入內(nèi)部編譯的依賴
如果想引入內(nèi)部編譯的依賴包可以通過includes屬性進(jìn)行添加,利用引用了內(nèi)部的dubbo可以通過如下配置:
配置完成后,再次執(zhí)行編譯:mvn clean package。
如果引入內(nèi)部包過多,也可以通過excludeGroupIds屬性去掉不變的依賴包。
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <layout>ZIP</layout> <!--去除在生產(chǎn)環(huán)境中不變的依賴--> <excludeGroupIds> org.springframework.boot, org.springframework, org.springframework.data, org.apache.tomcat.embed </excludeGroupIds> </configuration> </plugin> </plugins> </build>
注:layout 必須是 ZIP 、 excludeGroupIds 中時(shí)忽略也是就需要打在外部的 jar。
3.1 剔除不需要的依賴
3.1.1 方式一:排除一個(gè)具體的maven 模塊,通過唯一的groupId和artifactId組合來實(shí)現(xiàn)。(如果有必要,可以加入classifier來唯一確認(rèn)。)
<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í)行時(shí),會(huì)自動(dòng)下載所有的依賴包,并且緩存到本地,默認(rèn)的緩存路徑是${user.home}/.m2/,你也可以使用如下命令在啟動(dòng)時(shí)修改依賴的路徑
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配置:
在項(xiàng)目中創(chuàng)建一個(gè)文件,放在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"> <!-- 必須寫,否則打包時(shí)會(huì)有 assembly ID must be present and non-empty 錯(cuò)誤 這個(gè)名字最終會(huì)追加到打包的名字的末尾,如項(xiàng)目的名字為 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> <!-- 壓縮包下是否生成和項(xiàng)目名相同的根目錄 --> <includeBaseDirectory>false</includeBaseDirectory> <dependencySets> <dependencySet> <!-- 不使用項(xiàng)目的artifact,第三方j(luò)ar不要解壓,打包進(jìn)zip文件的lib目錄 --> <useProjectArtifact>false</useProjectArtifact> <outputDirectory>lib</outputDirectory> <unpack>false</unpack> </dependencySet> </dependencySets> <fileSets> <!-- 把項(xiàng)目相關(guān)的說明文件,打包進(jìn)zip文件的根目錄 --> <fileSet> <directory>${project.basedir}</directory> <outputDirectory></outputDirectory> <includes> <include>README*</include> <include>LICENSE*</include> <include>NOTICE*</include> </includes> </fileSet> <!-- 把項(xiàng)目的配置文件,打包進(jìn)zip文件的config目錄 --> <fileSet> <directory>${project.basedir}/src/main/resources</directory> <outputDirectory>config</outputDirectory> </fileSet> <!-- 把項(xiàng)目的腳本文件,打包進(jìn)zip文件的bin目錄 --> <fileSet> <directory>${project.basedir}/src/main/bin</directory> <outputDirectory>bin</outputDirectory> </fileSet> <!-- 把項(xiàng)目自己編譯出來的jar文件,打包進(jìn)zip文件的根目錄 --> <fileSet> <directory>${project.build.directory}</directory> <outputDirectory></outputDirectory> <includes> <include>*.jar</include> </includes> </fileSet> </fileSets> </assembly>
maven中的配置:
<build> <plugins> <!-- 指定啟動(dòng)類,將依賴打成外部jar包 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <!-- 生成的jar中,不要包含pom.xml和pom.properties這兩個(gè)文件 --> <addMavenDescriptor>false</addMavenDescriptor> <manifest> <!-- 是否要把第三方j(luò)ar放到manifest的classpath中 --> <addClasspath>true</addClasspath> <!-- 外部依賴jar包的最終位置 --> <classpathPrefix>lib/</classpathPrefix> <!-- 項(xiàng)目啟動(dòng)類 --> <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ā)布時(shí),跳過單元測試 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <skipTests>true</skipTests> </configuration> </plugin> </plugins> </build>
最終打包后的效果:
壓縮包里的文件內(nèi)容:
config配置文件:
六、SpringBoot剔除靜態(tài)文件
靜態(tài)文件排除到項(xiàng)目外面,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 打包時(shí)排除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>
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java Excel透視表相關(guān)操作實(shí)現(xiàn)代碼
這篇文章主要介紹了Java Excel透視表相關(guān)操作實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08springboot如何獲取application.yml里值的方法
這篇文章主要介紹了springboot如何獲取application.yml里的值,文章圍繞主題相關(guān)自資料展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-04-04IDEA去除掉代碼中虛線、波浪線和下劃線實(shí)線的方法
初次安裝使用IDEA,總是能看到導(dǎo)入代碼后,出現(xiàn)很多的波浪線,下劃線和虛線,這是IDEA給我們的一些提示和警告,但是有時(shí)候我們并不需要,反而會(huì)讓人看著很不爽,這里簡單記錄一下自己的調(diào)整方法,供其他的小伙伴在使用的時(shí)候參考2024-09-09Java實(shí)現(xiàn)中文算數(shù)驗(yàn)證碼的實(shí)現(xiàn)示例(算數(shù)運(yùn)算+-*/)
這篇文章主要介紹了Java實(shí)現(xiàn)中文算數(shù)驗(yàn)證碼的實(shí)現(xiàn)示例(算數(shù)運(yùn)算+-*/),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07Java與Spring?boot后端項(xiàng)目Bug超全總結(jié)
Spring Boot是一個(gè)開源的 Java 開發(fā)框架,它的目的是簡化Spring應(yīng)用程序的開發(fā)和部署,下面這篇文章主要給大家介紹了關(guān)于Java與Spring?boot后端項(xiàng)目Bug的相關(guān)資料,文中通過圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06Java刪除ArrayList中的重復(fù)元素的兩種方法
在Java編程中,ArrayList是一種常用的集合類,它允許我們存儲(chǔ)一組元素,在某些情況下,我們可能需要移除其中重復(fù)的元素,只保留唯一的元素,下面介紹兩種常見的刪除ArrayList中重復(fù)元素的方法,需要的朋友可以參考下2024-12-12Java通過調(diào)用C/C++實(shí)現(xiàn)的DLL動(dòng)態(tài)庫——JNI的方法
這篇文章主要介紹了Java通過調(diào)用C/C++實(shí)現(xiàn)的DLL動(dòng)態(tài)庫——JNI的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-01-01springmvc—handlermapping三種映射方式
這篇文章主要介紹了springmvc—handlermapping三種映射方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09