一、问题背景
Spring Boot 项目部署起来虽然已经简单很多了,但是一个运行 jar 包动辄几十上百兆,如果服务器是本地或者在内网还好,如果需要在公网环境部署,每次发布部署时都重新上传 Spring Boot 的 jar 包,因为网速的限制,也挺令人头大的。
二、解构 Spring Boot 的 jar 包
如果我们使用工具打开 Spring Boot 项目编译出来的 jar 文件,会发现占用磁盘空间的主要是外部依赖包,位于 jar 包内的 BOOT-INF/lib 路径下。
大多数情况对服务进行重新部署的时候,外部依赖库基本上都是不变的,所以这部分的上传是费时的重复操作。我们可以避免这部分不必要的重复。
三、解决办法
将外部依赖库从 Spring Boot 的 jar 包中分离出来,将外部依赖库单独上传服务器,以后每次更新部署的时候只需要上传瘦身后的 jar 文件,从此上传就是爽快的秒操作。
- 第一步:修改 Spring Boot 项目的 pom.xml 的插件配置如下,编译出不包含外部依赖库的 jar 包:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<includes>
<include>
<groupId>nothing</groupId>
<artifactId>nothing</artifactId>
</include>
</includes>
</configuration>
</plugin>
新编译出 jar 文件后,你会惊喜地发现原来动辄上百兆的文件,瞬间只有几百 K 的大小。
- 第二步:在项目根目录下执行如下 mvn 命令,获得外部依赖包
mvn dependency:copy-dependencies -DoutputDirectory=.\target\lib -Dinclude
=runtime
- 第三步:将外部依赖包和 Spring Boot 服务 jar 包上传服务器,在 jar 服务启动命令添加 loader.path 参数指向 外部依赖包的位置路径:
java -Dloader.path=<外部依赖包位置路径> -jar spring-boot-demo-0.0.1-SNAPSHOT.jar
当需要再次部署的时候,只需要编译打包 Spring Boot 服务 jar 包,然后上传服务器重启服务就行了,大大提高了效率。
四、一个服务器运行多个 Spring Boot 服务的情况
一般情况下,多个Spring Boot 服务的外部依赖库很多是重复的,也可以将多个 Spring Boot 服务的外部依赖库放到同一个目录位置,在编译打包 Spring Boot 项目的时候,修改 pom 文件的插件配置如下,使编译的 jar 文件中产生的 META-INF\MANIFEST.MF 文件中,包含运行时所需要加载的外部依赖库的信息,然后启动服务时,会自动按照 jar 文件中的信息加载外部依赖库:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<!-- 指定项目启动主类,一般不需要,根据实际情况指定 -->
<!-- <mainClass>${project.main.class}</mainClass> -->
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
上面的示例中,外部依赖库的 lib 文件夹和可运行的服务的 jar 文件位于同一目录,这样运行 jar 文件时,跟根据 MANIFEST.MF 文件的依赖库路径信息从当前目录下的 lib 目录下加载需要的包文件,此时启动命令不需要 loader.path 参数。
【完】