Springboot 瘦身打包

在实际生产过程中,常常会遇到资源打包文件太大,在部署的时候很不方便,传统的打包方式会将所有的文件(jar 和 资源配置文件)都打包在了一个 jar 文件中,这样后期在修改配置信息的时候,相当的不方便。

因此,如何将项目本身的 jar 文件和依赖 jar 文件分离,并把资源文件也分离出来,分别独立成自己的文件目录是很有必要的。

一、传统打包方式

传统的打包方式在 pom.xml 中只需要在 build 插件中配置spring-boot-maven-plugin即可:

1
2
3
4
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

打包出来的 jar 包是下面这样的,项目生成了target文件目录,里面打包好了一个完整的 jar 文件,直接使用:java -jar king-0.0.1-SNAPSHOT.jar就能运行了。

打成完整 jar 包

将 jar 包解压开,会得到下面的目录结构:

打成完整 jar 包内部结构

注意在META-INF文件目录中存在MANIFEST.MF文件,里面记录了启动类Start-Class 、依赖类Spring-Boot-Lib等信息。

运行 jar 包的时候,首先启动的是启动类,并依赖BOOT-INF/lib/文件目录中的 jar 文件。

二、springboot 打包 lib 和资源文件分离

现在有如下的打包需求:

项目自身 jar 文件打包在 zip 包根目录下,其他所有依赖包打包在lib文件夹下,资源文件全部打包在resources文件目录下,项目运行的脚本文件打包在bin文件目录下。

项目打包需求

pom.xml配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<build>
<plugins>
<!--打包jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<!--不打包资源文件 -->
<excludes>
<exclude>bin/**</exclude>
<exclude>config/**</exclude>
<exclude>static/**</exclude>
<exclude>templates/**</exclude>
<exclude>*.yml</exclude>
</excludes>
<!--
<archive>
<manifest>
<addClasspath>true</addClasspath>
// MANIFEST.MF 中 Class-Path 加入前缀,不需要配置,在外部命令行指定
<classpathPrefix>lib/</classpathPrefix>
// jar包不包含唯一版本标识
<useUniqueVersions>false</useUniqueVersions>
` // 指定入口类,如果程序里只有一个main方法就建议不要手动配置
<mainClass>org.woodwhale.king.KingApplication</mainClass>
</manifest>
<manifestEntries>
// MANIFEST.MF 中 Class-Path 加入资源文件目录,不需要配置,在外部命令行指
<Class-Path>./resources/</Class-Path>
</manifestEntries>
</archive>
-->

<!--
生成到 target 目录下,
也可以放到指定目录,例如: ${project.build.directory}/boot
-->
<outputDirectory>${project.build.directory}</outputDirectory>
</configuration>
</plugin>

<!--拷贝依赖 copy-dependencies -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>

<!--拷贝资源文件 copy-resources -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<outputDirectory>${project.build.directory}/resources</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>

<!--
spring boot repackage,
依赖 maven-jar-plugin 打包的jar包
重新打包成 spring boot的jar包
-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 包含本地 jar 依赖 -->
<includeSystemScope>true</includeSystemScope>
<!-- 重写包含依赖,包含不存在的依赖,jar里没有pom里的依赖 -->
<includes>
<include>
<groupId>null</groupId>
<artifactId>null</artifactId>
</include>
</includes>
<layout>ZIP</layout>
<!-- 使用外部配置文件,jar包里没有资源文件 -->
<addResources>true</addResources>
<outputDirectory>${project.build.directory}</outputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<!--
配置jar包特殊标识 配置后,保留原文件,
生成新文件 *-run.jar 配置jar包特殊标识 不配置,
原文件命名为 *.jar.original,生成新文件 *.jar
-->
<classifier>run</classifier>
</configuration>
</execution>
</executions>
</plugin>

<!-- 打包发布时,跳过单元测试 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>

<!-- 使用assembly打zip包 -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<!-- assembly配置文件位置 -->
<descriptor>assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

注意:在spring-boot-maven-plugin中配置了包含本地jar依赖的配置<includeSystemScope>true</includeSystemScope>

在项目根目录下存在放置assembly.xml配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<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">
<id>${project.artifactId}</id>
<formats>
<format>zip</format>
</formats>
<!-- 压缩包下是否生成和项目名相同的根目录 -->
<includeBaseDirectory>false</includeBaseDirectory>
<baseDirectory>${project.artifactId}-${project.version}</baseDirectory>

<fileSets>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory></outputDirectory>
<includes>
<include>${artifact.artifactId}-${artifact.version}.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>${file.separator}resources</outputDirectory>
</fileSet>
<fileSet>
<directory>bin/</directory>
<outputDirectory>${file.separator}bin</outputDirectory>
</fileSet>
<fileSet>
<directory>lib/</directory>
<outputDirectory>${file.separator}lib</outputDirectory>
</fileSet>
</fileSets>

<!-- 使用项目的artifact,第三方 jar 打包进zip文件的 lib 目录 -->
<dependencySets>
<dependencySet>
<useProjectArtifact>true</useProjectArtifact>
<outputDirectory>lib</outputDirectory>
<scope>runtime</scope>
<includes>
<include>*:*</include>
</includes>
<excludes>
<exclude>${groupId}:${artifactId}</exclude>
<exclude>org.springframework.boot:spring-boot-devtools</exclude>
</excludes>
</dependencySet>
</dependencySets>
</assembly>

bin脚本文件目录中放置脚本文件和配置文件:

配置文件用于指定 jar 的名称及版本信息

1
2
ARTIFACTID=king
VERSION=0.0.1

windows 脚本start.bat文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
ECHO off

ECHO "checking config.ini..."

SET ROOT=%~dp0
SET CONFIG_FILE=%ROOT%config.ini
REM **从配置文件中读取内容**
FOR /F "tokens=1,2 delims==" %%i IN (%CONFIG_FILE%) DO (
SET %%i=%%j
)
SET APP_NAME=%ARTIFACTID%-%VERSION%

IF "%APP_NAME%" == "" (
ECHO "this config.ini is not exist,please check this config file."
GOTO End
) ELSE (
ECHO "checking JAVA_HOME config from checking config.ini..."
GOTO OkPath
)

:OkPath
echo "check java_home..."
if not "%JAVA_HOME%" == "" GOTO OkJHome

:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" GOTO Runserver

:Runserver
SET JAR_NAME=%APP_NAME%.jar
SET APP_CONFIG=-Dloader.path=.,resources,lib

ECHO "111:%RUN_JAVA%"
ECHO "Starting the %JAR_NAME%"

ECHO "%JAVA_HOME%\bin\java -Xms512m -Xmx512m -jar %APP_CONFIG% %JAR_NAME%"
CD ..
%JAVA_HOME%\bin\java.exe -Xms512m -Xmx512m -jar %APP_CONFIG% %JAR_NAME%
GOTO End

:End
PAUSE

脚本解释:最为关键的就是运行jar 文件的时候携带-Dloader.path=.,resources,lib

linux 脚本startup.sh文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/bin/sh

## java env
export JAVA_HOME=/usr/java/jdk1.8.0_162
API_NAME=./king-0.0.1.jar

export JRE_HOME=JAVA_HOME/jre
API_CONFIG=.,resources,lib
JAR_NAME=$API_NAME.jar
PID=$API_NAME.pid

usage() {
echo "Usage: sh startup.sh [start|stop|restart|status]"
exit 1
}

is_exist(){
pid=`ps -ef|grep $JAR_NAME|grep -v grep|awk '{print $2}' `
if [ -z "${pid}" ]; then
return 1
else
return 0
fi
}

start(){
is_exist
if [ $? -eq "0" ]; then
echo ">>> ${JAR_NAME} is already running PID=${pid} <<<"
else
nohup $JRE_HOME/bin/java -Xms256m -Xmx512m -jar -Dloader.path=$API_CONFIG $JAR_NAME >/dev/null 2>&1 &
echo $! > $PID
echo ">>> start $JAR_NAME successed PID=$! <<<"
fi
}

stop(){
#is_exist
pidf=$(cat $PID)
#echo "$pidf"
echo ">>> api PID = $pidf begin kill $pidf <<<"
kill $pidf
rm -rf $PID
sleep 2
is_exist
if [ $? -eq "0" ]; then
echo ">>> api 2 PID = $pid begin kill -9 $pid <<<"
kill -9 $pid
sleep 2
echo ">>> $JAR_NAME process stopped <<<"
else
echo ">>> ${JAR_NAME} is not running <<<"
fi
}

status(){
is_exist
if [ $? -eq "0" ]; then
echo ">>> ${JAR_NAME} is running PID is ${pid} <<<"
else
echo ">>> ${JAR_NAME} is not running <<<"
fi
}

restart(){
stop
start
}

case "$1" in
"start")
start
;;
"stop")
stop
;;
"status")
status
;;
"restart")
restart
;;
*)
usage
;;
esac
exit 0

shell 脚本里需要配置JAVA_HOME路径和 jar 文件名即可。

updated updated 2020-03-01 2020-03-01
本文结束感谢阅读

本文标题:Springboot 瘦身打包

本文作者:木鲸鱼

微信公号:木鲸鱼 | woodwhales

原始链接:https://woodwhales.cn/2018/12/16/006/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%