1 背景介绍
随着Spring Boot的流行,大家体验到只需构建输出一个jar文件,然后只需一个java -jar命令就能部署运行应用的爽快。常见一些单体应用随着项目规模的扩展单个jar文件的大小越来越大,动辄两三百MB。如果再引入微服务架构,动辄一二十个微服务,所有模块jar加起来整个系统光部署文件就一两个GB。
一个系统一旦上线运行,无论新需求迭代还是Bug修复,免不了需要做部署更新,尤其对于一些交付类型项目,首次部署或异地更新, 动不动就需要传输几百MB或几个GB的部署文件,确实是一个让人头疼的问题。
可以想象一下,线上系统发现一个紧急严重Bug捅到了主管那里,交代马上紧急修复解决,研发同事火速分析排查分分钟搞定提交代码并完成构建打包并交付给运维。过一会领导着急上火来过问问题更新解决了吗?运维只能很尴尬的回答:还没呢,部署包文件比较大,正在上传有点慢…
一听领导就火了,就改了几行代码,部署更新为啥要上传几百MB的文件呢?难道没有办法优化一下吗?
2 技术原理剖析
使用maven 打包将公共的依赖jar 包打到对应的目录下,引用对应目录下的东西
3 实现步骤
3.1 正常打包
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<mainClass>com.****.AppmainClass>
<layout>ZIPlayout>
configuration>
<executions>
<execution>
<goals>
<goal>repackagegoal>
goals>
execution>
executions>
plugin>
<plugins>
<build>
3.2 编辑pom文件将依赖打包到lib目录下
<build>
<finalName>${project.artifactId}</finalName>
<!--
特别注意:
项目仅仅是为了演示配置方便,直接在parent的build部分做了插件配置和运行定义。
但是实际项目中需要把这些定义只放到spring boot模块项目(可优化使用pluginManagement形式),避免干扰其他util、common等模块项目
-->
<plugins>
<!-- 基于maven-jar-plugin插件实现把依赖jar定义写入输出jar的META-INFO/MANIFEST文件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<useUniqueVersions>false</useUniqueVersions>
</manifest>
<manifestEntries>
<!--
有些供应商的sdk jar在pom中是以systemPath方式引入的,maven-jar-plugin组件没有直接参数声明包含指定scope的组件
通过使用额外定义 Class-Path 值来追加指定依赖组件列表,在子模块按实际情况指定 jar-manifestEntries-classpath 值即可
例如(注意前面个点字符及各空格分隔符):. lib/xxx-1.0.0.jar lib/yyy-2.0.0.jar
详见各子模块中 jar-manifestEntries-classpath 属性定义示例
-->
<Class-Path>${jar-manifestEntries-classpath}</Class-Path>
</manifestEntries>
</archive>
</configuration>
</plugin>
<!-- 拷贝项目所有依赖jar文件到构建lib目录下 -->
<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>
<!--
各子模块按照实际层级定义各模块对应的属性值,检查所有微服务模块依赖jar文件合并复制到同一个目录
详见各子模块中 boot-jar-output 属性定义
-->
<outputDirectory>${boot-jar-output}/lib</outputDirectory>
<excludeTransitive>false</excludeTransitive>
<stripVersion>false</stripVersion>
<silent>false</silent>
</configuration>
</execution>
<execution>
<id>copy-resource</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<!--
各子模块按照实际层级定义各模块对应的属性值,检查所有微服务模块依赖jar文件合并复制到同一个目录
详见各子模块中 boot-jar-output 属性定义
-->
<outputDirectory>${boot-jar-output}/lib</outputDirectory>
<excludeTransitive>false</excludeTransitive>
<stripVersion>false</stripVersion>
<silent>false</silent>
</configuration>
</execution>
</executions>
</plugin>
<!-- Spring Boot模块jar构建 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includes>
<!-- 不存在的include引用,相当于排除所有maven依赖jar,没有任何三方jar文件打入输出jar -->
<include>
<groupId>null</groupId>
<artifactId>null</artifactId>
</include>
</includes>
<layout>ZIP</layout>
<!--
基于maven-jar-plugin输出微服务jar文件进行二次spring boot重新打包文件的输出目录
所有微服务构建输出jar文件统一输出到与lib同一个目录,便于共同引用同一个lib目录
详见各子模块中boot-jar-output属性定义
-->
<outputDirectory>${boot-jar-output}</outputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
3.3 打完包的文件
3.4 首次部署项目时 需要将lib目录和jar同时上传
3.5 后续更新更新自己编译的class文件了
4.启动jar
配置文件分离的话,需要在启动命令中添加指定文件
java -jar hello_springboot-0.0.1-SNAPSHOT.jar --spring.config.location=D:/application.properties