Skip to content

Spring Boot Maven Plugin 打包可执行归档文件详解 📦

概述

Spring Boot Maven Plugin 的核心功能之一就是创建可执行的归档文件(JAR 或 WAR),这些文件包含了应用程序的所有依赖,可以通过 java -jar 命令直接运行。这个功能解决了传统 Java 应用部署时需要单独管理依赖的痛点。

IMPORTANT

可执行归档文件是 Spring Boot 应用部署的核心特性,它将应用程序及其所有依赖打包成一个独立的文件,极大简化了部署过程。

为什么需要可执行归档文件? 🤔

在传统的 Java 应用开发中,我们面临以下痛点:

bash
# 需要手动管理 classpath
java -cp "lib/*:app.jar" com.example.Application

# 部署时需要确保所有依赖都在正确位置
├── app.jar
├── lib/
   ├── spring-core-5.3.21.jar
   ├── spring-boot-2.7.0.jar
   └── ... (数十个依赖文件)
bash
# 一个命令搞定
java -jar my-app.jar

# 部署时只需要一个文件
└── my-app.jar (包含所有依赖)

基本配置与使用

1. 基础配置

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

TIP

如果你使用 spring-boot-starter-parent,上述配置已经预配置好了,你只需要添加插件定义即可。

2. 执行打包

bash
# 正确的执行方式
mvn package spring-boot:repackage

# 或者直接使用(如果使用了 starter-parent)
mvn package

WARNING

repackage 目标不能单独在命令行使用,它需要操作 package 阶段生成的源 JAR 文件。

可执行归档文件的内部结构 🏗️

让我们通过一个实际的 Kotlin Spring Boot 项目来理解可执行 JAR 的结构:

kotlin
package com.example.demo

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args) 
}
kotlin
package com.example.demo.controller

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class HelloController {
    
    @GetMapping("/hello")
    fun hello(): String {
        return "Hello from Spring Boot!"
    }
}

打包后的 JAR 文件结构:

my-app.jar
├── META-INF/
│   └── MANIFEST.MF                 # 包含 Main-Class 和 Start-Class
├── BOOT-INF/
│   ├── classes/                    # 应用程序类文件
│   │   └── com/example/demo/
│   │       ├── DemoApplication.class
│   │       └── controller/
│   │           └── HelloController.class
│   ├── lib/                        # 所有依赖 JAR 文件
│   │   ├── spring-boot-3.2.0.jar
│   │   ├── spring-core-6.1.0.jar
│   │   └── ... (其他依赖)
│   └── layers.idx                  # 分层信息(可选)
└── org/springframework/boot/loader/ # Spring Boot 加载器

分层 JAR/WAR 🎯

分层归档是 Spring Boot 的一个重要特性,特别适用于 Docker 镜像构建场景。

默认分层策略

NOTE

分层的顺序很重要:变化频率低的层应该放在前面,这样在 Docker 构建时可以更好地利用缓存。

自定义分层配置

点击查看完整的分层配置示例
xml
<!-- pom.xml 中的配置 -->
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <layers>
            <enabled>true</enabled>
            <configuration>${project.basedir}/src/layers.xml</configuration> 
        </layers>
    </configuration>
</plugin>
xml
<!-- src/layers.xml -->
<layers xmlns="http://www.springframework.org/schema/boot/layers"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/boot/layers
                          https://www.springframework.org/schema/boot/layers/layers-3.5.xsd">
    <application>
        <into layer="spring-boot-loader">
            <include>org/springframework/boot/loader/**</include>
        </into>
        <into layer="application" />
    </application>
    <dependencies>
        <into layer="application">
            <includeModuleDependencies />
        </into>
        <into layer="snapshot-dependencies">
            <include>*:*:*SNAPSHOT</include>
        </into>
        <into layer="company-dependencies">
            <include>com.example:*</include> 
        </into>
        <into layer="dependencies" />
    </dependencies>
    <layerOrder>
        <layer>dependencies</layer>
        <layer>spring-boot-loader</layer>
        <layer>snapshot-dependencies</layer>
        <layer>company-dependencies</layer>
        <layer>application</layer>
    </layerOrder>
</layers>

实际应用场景示例 💼

场景 1:微服务架构中的服务模块

kotlin
// 用户服务
@SpringBootApplication
class UserServiceApplication

@RestController
class UserController {
    
    @GetMapping("/users/{id}")
    fun getUser(@PathVariable id: Long): User {
        // 业务逻辑
        return userService.findById(id) 
    }
}
xml
<!-- 配置为可执行 JAR -->
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <mainClass>com.example.user.UserServiceApplication</mainClass> 
    </configuration>
</plugin>

场景 2:作为依赖模块的库项目

当你的模块需要被其他项目依赖时,应该使用自定义分类器:

xml
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>repackage</id>
            <goals>
                <goal>repackage</goal>
            </goals>
            <configuration>
                <classifier>exec</classifier> 
            </configuration>
        </execution>
    </executions>
</plugin>

这样会生成两个文件:

  • my-library-1.0.jar - 普通 JAR,可作为依赖使用
  • my-library-1.0-exec.jar - 可执行 JAR

场景 3:排除特定依赖

在某些情况下,你可能需要排除一些不必要的依赖:

xml
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <excludes>
            <exclude>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId> 
            </exclude>
        </excludes>
    </configuration>
</plugin>

常见配置选项详解 ⚙️

1. 布局类型 (Layout)

布局类型描述适用场景
JAR标准可执行 JAR 布局大多数 Spring Boot 应用
WAR可执行 WAR 布局需要部署到 Servlet 容器的应用
ZIP使用 PropertiesLauncher需要外部配置的应用
NONE仅打包依赖,不包含启动器特殊用途的库项目

2. 重要参数说明

核心参数

  • mainClass: 指定主类,如果不指定会自动查找包含 main 方法的类
  • classifier: 为重新打包的归档文件添加分类器
  • excludeDevtools: 默认为 true,排除开发工具
  • executable: 创建完全可执行的 JAR(适用于 Unix 系统)

最佳实践建议 🌟

1. 项目结构建议

kotlin
src/
├── main/
│   ├── kotlin/
│   │   └── com/example/
│   │       ├── Application.kt          # 主启动类
│   │       ├── config/                 # 配置类
│   │       ├── controller/             # 控制器
│   │       ├── service/                # 服务层
│   │       └── repository/             # 数据访问层
│   └── resources/
│       ├── application.yml             # 应用配置
│       └── static/                     # 静态资源
└── test/

2. 配置模板

xml
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <!-- 基础配置 -->
        <mainClass>${start.class}</mainClass>
        
        <!-- 分层配置 -->
        <layers>
            <enabled>true</enabled>
        </layers>
        
        <!-- 排除开发依赖 -->
        <excludeDevtools>true</excludeDevtools>
        <excludeDockerCompose>true</excludeDockerCompose>
        
        <!-- 自定义排除 -->
        <excludes>
            <exclude>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </exclude>
        </excludes>
    </configuration>
</plugin>

常见问题与解决方案 🔧

问题 1: 找不到主类

bash
# 错误信息
no main manifest attribute, in my-app.jar

解决方案:

xml
<configuration>
    <mainClass>com.example.Application</mainClass> 
</configuration>

问题 2: 依赖冲突

bash
# 错误信息
java.lang.NoSuchMethodError: ...

解决方案:

xml
<configuration>
    <excludes>
        <exclude>
            <groupId>conflicting.group</groupId>
            <artifactId>conflicting-artifact</artifactId> 
        </exclude>
    </excludes>
</configuration>

总结 📝

Spring Boot Maven Plugin 的 repackage 功能是现代 Java 应用部署的核心工具:

优势:

  • 简化部署流程,一个文件包含所有依赖
  • 支持分层架构,优化 Docker 镜像构建
  • 灵活的配置选项,适应不同场景需求
  • 自动处理类路径和启动逻辑

适用场景:

  • 微服务架构的独立服务
  • 容器化部署的应用
  • 需要简化部署的企业应用
  • 云原生应用开发

通过合理配置 Spring Boot Maven Plugin,你可以轻松创建高效、可维护的可执行归档文件,大大简化应用的构建和部署过程。 🚀