SpringBoot Maven打包优化

SpringBoot Maven打包优化

Spring Boot 简化了配置,但日志管理依然需要重视。日志配置、链路追踪、排查思路都是日常开发中会遇到的问题。本文讲实际项目中的日志管理经验。

一、SpringBoot打包基础

#

1.1 传统打包方式的问题

普通Maven打包会产生两个问题:

问题一:依赖分散

target/
├── classes/ # 编译后的类文件
├── lib/ # 依赖jar(如果有配置)
└── myapp.jar # 仅包含项目代码

这种方式需要额外处理依赖,不适合直接部署。

问题二:可执行jar的结构
SpringBoot的fat jar虽然包含所有依赖,但默认结构不够优化:

myapp.jar
├── META-INF/
│ └── MANIFEST.MF
├── BOOT-INF/
│ ├── classes/ # 项目代码
│ └── lib/ # 所有依赖jar
└── org/springframework/boot/loader/

#

1.2 spring-boot-maven-plugin基础配置

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

repackage目标会将普通jar重新打包为可执行的fat jar。

二、分层打包(Layered Jars)

SpringBoot 2.3+ 引入了分层打包,利用Docker的缓存层机制加速构建。

#

2.1 启用分层打包

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>

#

2.2 分层结构解析

启用后,jar内部会按以下层次组织:

BOOT-INF/layers.idx
├── dependencies # 稳定不变的第三方依赖
├── spring-boot-loader # SpringBoot加载器
├── snapshot-dependencies # SNAPSHOT依赖
├── application # 频繁变动的项目代码和配置

#

2.3 提取分层内容

使用layertools模式提取:

java -Djarmode=layertools -jar myapp.jar extract

生成的目录结构:

extracted/
├── dependencies/ # 第三方依赖
├── spring-boot-loader/ # 加载器
├── snapshot-dependencies/ # SNAPSHOT依赖
└── application/ # 项目代码

#

2.4 配合Dockerfile使用

FROM eclipse-temurin:17-jre as builder
WORKDIR /application
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM eclipse-temurin:17-jre
WORKDIR /application
COPY --from=builder /application/dependencies/ ./
COPY --from=builder /application/spring-boot-loader/ ./
COPY --from=builder /application/snapshot-dependencies/ ./
COPY --from=builder /application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

优势

  • 依赖层不常变化,Docker缓存命中率高
  • 只有代码变更时才需要重新推送application层
  • 大幅减少镜像构建和推送时间

三、构建配置优化

#

3.1 排除开发依赖

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeDevtools>true</excludeDevtools>
</configuration>
</plugin>

#

3.2 排除特定依赖

<configuration>
<excludes>
<exclude>
<groupId>com.example</groupId>
<artifactId>test-util</artifactId>
</exclude>
</excludes>
</configuration>

#

3.3 自定义Main-Class

<configuration>
<mainClass>com.example.MyApplication</mainClass>
</configuration>

#

3.4 生成构建信息

<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>

自动生成META-INF/build-info.properties

build.artifact=myapp
build.group=com.example
build.name=My Application
build.time=2024-07-13T09:00:00.000Z
build.version=1.0.0

代码中获取:

@Autowired
private BuildProperties buildProperties;

public void printInfo() {
System.out.println("Version: " + buildProperties.getVersion());
System.out.println("Build Time: " + buildProperties.getTime());
}

四、构建加速技巧

#

4.1 Maven并行构建

mvn clean package -T 4

使用4个线程并行构建模块。

#

4.2 跳过测试加速

开发环境验证时:

mvn clean package -DskipTests

#

4.3 配置镜像加速

settings.xml中配置阿里云镜像:

<mirrors>
<mirror>
<id>aliyun</id>
<name>Aliyun Maven</name>
<url>https://maven.aliyun.com/repository/public</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>

#

4.4 增量编译

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<useIncrementalCompilation>true</useIncrementalCompilation>
</configuration>
</plugin>

#

4.5 Gradle替代方案

Gradle的增量构建通常更快:

plugins {
id 'java'
id 'org.springframework.boot' version '3.x.x'
}

tasks.named('bootJar') {
layered {
enabled = true
}
}

五、打包瘦身策略

#

5.1 精简依赖

分析依赖树

mvn dependency:tree

排除传递依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>

替换为Undertow(更轻量):

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

#

5.2 使用JRE运行时

Dockerfile中使用JRE而非JDK:

FROM eclipse-temurin:17-jre

#

5.3 自定义JRE(Java 11+)

使用jlink创建最小化JRE:

jlink \
--module-path $JAVA_HOME/jmods \
--add-modules java.base,java.logging,java.xml,jdk.unsupported \
--output myjre \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2

六、多环境配置

#

6.1 Profile过滤资源

<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<env>dev</env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env>prod</env>
</properties>
</profile>
</profiles>

#

6.2 资源过滤

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>

七、完整优化配置示例

<?xml version="1.0" encoding="UTF-8"?>
<project>
<!-- ... 其他配置 ... -->

<build>
<finalName>${project.artifactId}</finalName>

<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>

<plugins>
<!-- SpringBoot打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- 启用分层 -->
<layers>
<enabled>true</enabled>
</layers>
<!-- 排除devtools -->
<excludeDevtools>true</excludeDevtools>
<!-- 包含运行时信息 -->
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- 编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>

<!-- 资源插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>

八、验证打包结果

#

8.1 检查jar内容

jar tf myapp.jar | head -20

#

8.2 验证可执行性

java -jar myapp.jar --dry-run

#

8.3 分析jar大小构成

jar tf myapp.jar | grep -E '^BOOT-INF/lib/' | \
awk -F'/' '{print $NF}' | sort

九、常见问题

#

9.1 打包后无法启动

检查MANIFEST.MF中的Main-Class:

unzip -p myapp.jar META-INF/MANIFEST.MF

正确应该指向:

Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.example.MyApplication

#

9.2 依赖版本冲突

使用Maven Enforcer插件:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<dependencyConvergence/>
</rules>
</configuration>
</execution>
</executions>
</plugin>

#

9.3 资源文件未打包

确保资源文件在src/main/resources目录下,且未被排除。

十、总结

优化方向 具体措施 适用场景
构建速度 并行构建、镜像加速、跳过测试 开发迭代
镜像大小 分层打包、JRE运行时、精简依赖 Docker部署
部署效率 layertools提取、缓存层复用 CI/CD流水线
可维护性 build-info、版本信息注入 生产运维

掌握这些Maven打包优化技巧,能够显著提升SpringBoot应用的构建和部署效率,特别是在容器化环境中,分层打包带来的收益尤为明显。

核心要点

  1. 日志级别设置:根据环境设置合适的级别

  2. 日志格式配置:添加 traceId 便于链路追踪

  3. 日志输出:控制台输出和文件输出的配置

  4. 日志归档:设置滚动策略和保留时间

总结

日志是排查问题的生命线,合理配置日志可以提升排查效率。在实际项目中,结合 ELK 等工具搭建日志系统,可以更好地管理和分析日志。


   转载规则


《SpringBoot Maven打包优化》 小乐 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录