Kyle's Notebook

Maven 打包依赖外置

Word count: 1.5kReading time: 7 min
2020/10/08

Maven 打包依赖外置

需求背景

最近发现公司的项目都是本地打包、上传到服务器,这些包动辄几十 M 甚至上百 M,而且因为没有做 CI/CD,有时网络状况不好每次上传都要几十分钟,服务数量太多时候部署也是一件很头疼的事。

实际上这些依赖大部分都是类似甚至可以共同使用的(比如 Spring 系列的包),完全没有必要在每个程序编译时都带上这些依赖。

因此可以考虑把这部分依赖包整理出来,存放在服务器上作为服务的共享库。

一般部署

以一个简单的 Spring Boot 应用为例,只有一个类文件 DemoApplication.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController("/")
@SpringBootApplication
public class DemoApplication {

@GetMapping
public String get() {
return "Hello, World!";
}

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- ... -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

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

打包后发现哪怕只有一个接口,可以单独运行的 jar 包占据 16M 之大:

1
2
3
4
$ mvn clean package
# ...
$ ll -h demo-0.0.1-SNAPSHOT.jar
-rw-r--r-- 1 yipwinghong 197609 16M 10月 8 22:23 demo-0.0.1-SNAPSHOT.jar

解压 demo-0.0.1-SNAPSHOT.jar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ ll -h BOOT-INF/lib/
total 16M
-rw-r--r-- 1 ywh 197609 67K 8月 2 18:14 jackson-annotations-2.11.2.jar
-rw-r--r-- 1 ywh 197609 344K 8月 2 18:16 jackson-core-2.11.2.jar
-rw-r--r-- 1 ywh 197609 1.4M 8月 2 18:36 jackson-databind-2.11.2.jar
-rw-r--r-- 1 ywh 197609 34K 8月 2 19:44 jackson-datatype-jdk8-2.11.2.jar
-rw-r--r-- 1 ywh 197609 109K 8月 2 19:44 jackson-datatype-jsr310-2.11.2.jar
-rw-r--r-- 1 ywh 197609 9.1K 8月 2 19:44 jackson-module-parameter-names-2.11.2.jar
-rw-r--r-- 1 ywh 197609 25K 8月 2 2019 jakarta.annotation-api-1.3.5.jar
-rw-r--r-- 1 ywh 197609 233K 8月 26 2019 jakarta.el-3.0.3.jar
-rw-r--r-- 1 ywh 197609 4.5K 12月 16 2019 jul-to-slf4j-1.7.30.jar
-rw-r--r-- 1 ywh 197609 286K 5月 10 12:07 log4j-api-2.13.3.jar
-rw-r--r-- 1 ywh 197609 18K 5月 10 12:10 log4j-to-slf4j-2.13.3.jar
# ...

只因其中包含了大量的 jar 包…= =

依赖外置

解决方式是打包时只包含自己的代码,而把依赖包导出。

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
<!-- ... -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.ywh.demo.DemoApplication</mainClass>
<layout>ZIP</layout>
<includes>
<include>
<groupId>nothing</groupId>
<artifactId>nothing</artifactId>
</include>
</includes>
</configuration>
</plugin>
<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>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
</resource>
</resources>
</build>
<!-- ... -->

重新打包:

1
mvn clean package

查看 target 输出的内容,可见 jar 包只剩下 121K 了,依赖包被导出到 target/lib 下(正好占据 16M,与之前的整包基本一致),应该没什么问题。

1
2
3
4
5
6
7
8
9
$ ll -h target/demo-0.0.1-SNAPSHOT.jar
-rw-r--r-- 1 yipwinghong 197609 121K 10月 8 22:36 demo-0.0.1-SNAPSHOT.jar
$ ll -h target/lib/
total 16M
-rw-r--r-- 1 ywh 197609 67K 10月 8 21:01 jackson-annotations-2.11.2.jar
-rw-r--r-- 1 ywh 197609 344K 10月 8 21:01 jackson-core-2.11.2.jar
-rw-r--r-- 1 ywh 197609 1.4M 10月 8 21:02 jackson-databind-2.11.2.jar
-rw-r--r-- 1 ywh 197609 34K 10月 8 21:01 jackson-datatype-jdk8-2.11.2.jar
# ...

但是因为缺少必要的依赖,这个包是不能跑起来的:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ java -jar target/demo-0.0.1-SNAPSHOT.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:49)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:107)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
at org.springframework.boot.loader.PropertiesLauncher.main(PropertiesLauncher.java:466)
Caused by: java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication
at com.ywh.demo.DemoApplication.main(DemoApplication.java:18)
... 8 more

要部署程序,在启动参数 -Dloader.path 指定依赖包导出的路径即可:

1
2
3
4
5
6
7
8
9
10
11
12
$ java -Dloader.path=target/lib/ -jar target/demo-0.0.1-SNAPSHOT.jar

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

2020-10-08 22:45:07.138 INFO 19232 --- [ main] com.ywh.demo.DemoApplication : Starting DemoApplication v0.0.1-SNAPSHOT on DESKTOP-N02L3UQ with PID 19232 (C:\Project\other-project\demo\target\demo-0.0.1-SNAPSHOT.jar started by yipwinghong in C:\Project\other-project\demo)
# ...

如果希望单独导出这些包到指定路径 ${LIB_PATH},可以执行:

1
mvn dependency:copy-dependencies -DoutputDirectory=${LIB_PATH}

总结

一般来说一个项目整体框架确定后,引入的依赖包不会有太大的改动,因此固定存放在服务器上的某个路径即可。

如果只是修改业务逻辑、重新部署时没有必要把依赖包也一并带上;

但如果涉及到 pom 文件结构、依赖的变更,则需要修改原来的 lib。

CATALOG
  1. 1. Maven 打包依赖外置
    1. 1.1. 需求背景
    2. 1.2. 一般部署
    3. 1.3. 依赖外置
    4. 1.4. 总结