1. 概述

在日常开发中,项目的构建配置(如 Maven 的 pom.xml 或 Gradle 的 build.gradle)通常包含大量元信息:版本号、项目名称、描述、构建时间等。这些信息不仅对构建系统有用,在应用运行时也常常需要暴露给外部,比如在监控接口、健康检查页或管理后台展示当前版本。

与其把这些信息硬编码到代码或配置文件中,不如直接从构建配置中提取。这样既能保证一致性,又能避免重复维护。

本文将介绍如何在 Spring Boot 项目中,优雅地将构建信息注入到应用内部并加以使用,涵盖 Maven 和 Gradle 两种构建工具的实践方式。


2. 构建信息示例

假设我们想在首页展示应用的名称和版本信息。这些内容通常已经定义在 pom.xml 中:

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <artifactId>spring-boot</artifactId>
    <name>spring-boot</name>
    <packaging>war</packaging>
    <description>This is simple boot application for Spring boot actuator test</description>
    <version>0.0.1-SNAPSHOT</version>
...
</project>

我们的目标是让 Spring Boot 应用能直接读取 project.descriptionproject.version 这类字段。


3. 在 application.properties 中引用构建属性

最简单的方式是在 application.properties 中通过占位符引用 Maven 属性:

application-description=@project.description@
application-version=@project.version@

关键点:使用 @xxx@ 包裹属性名,Spring Boot 会在打包时自动替换为 pom.xml 中的实际值。

这个过程叫做 资源过滤(Resource Filtering),由 Maven 的 maven-resources-plugin 完成。

⚠️ 注意事项:

  • ❌ 该机制仅对 src/main/resources 生效src/test/resources 不会触发过滤。
  • ❌ 如果你使用 spring-boot:run 并设置了 addResources=true,会直接加载源目录资源,绕过过滤机制,导致占位符未被替换。
  • ✅ 此功能开箱即用的前提是:**继承了 spring-boot-starter-parent**。

3.1 不使用 spring-boot-starter-parent 时的配置

如果你的项目没有继承 spring-boot-starter-parent(比如多模块聚合项目),需要手动启用资源过滤。

1. 启用 filtering

pom.xml 中显式开启资源过滤:

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

2. 配置 maven-resources-plugin 分隔符

为了避免与 Spring 的 ${} 占位符冲突,建议自定义分隔符并关闭默认行为:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <configuration>
        <delimiters>
            <delimiter>@</delimiter>
        </delimiters>
        <useDefaultDelimiters>false</useDefaultDelimiters>
    </configuration>
</plugin>

useDefaultDelimiters=false 可防止 ${} 被 Maven 提前解析,保留给 Spring 容器处理。


4. 在 YAML 文件中使用构建属性

YAML 对 @ 字符敏感(它是 YAML 的锚点语法),直接使用 @xxx@ 会导致解析失败。

解决方法有两种:

方案一:更换 Maven 的占位符分隔符

@ 改为其他符号,比如 ^

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <configuration>
        <delimiters>
            <delimiter>^</delimiter>
        </delimiters>
        <useDefaultDelimiters>false</useDefaultDelimiters>
    </configuration>
</plugin>

然后在 application.yml 中使用:

application-description: ^project.description^
application-version: ^project.version^

方案二:通过 properties 全局覆盖

更简洁的方式是在 pom.xml 中设置:

<properties>
    <resource.delimiter>^</resource.delimiter>
</properties>

效果相同,但配置更集中。


5. 使用 BuildProperties Bean 获取构建信息

上面的方法适合静态配置注入,但如果想在 Java 代码中动态获取构建信息(如版本、构建时间等),推荐使用 Spring Boot 内置的 BuildProperties 类。

5.1 Maven 配置

pom.xml 中为 spring-boot-maven-plugin 添加 build-info 目标:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>build-info</id>
                    <goals>
                        <goal>build-info</goal>
                    </goals>
                    <configuration>
                        <additionalProperties>
                            <java.version>${java.version}</java.version>
                            <description>${project.description}</description>
                            <custom.value>123</custom.value>
                        </additionalProperties>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

构建后,会在 target/classes/META-INF/build-info.properties 生成如下内容:

build.artifact=spring-boot-properties
build.custom.value=123
build.description=Spring Boot Properties Module
build.group=com.baeldung.spring-boot-modules
build.java.version=11
build.name=spring-boot-properties
build.time=2023-04-08T04:29:28.905Z
build.version=0.0.1-SNAPSHOT

✅ 自动生成 build.time,非常实用。

5.2 在代码中访问 BuildProperties

Spring Boot 会自动将 build-info.properties 加载为 BuildProperties Bean。

直接注入即可使用:

@Autowired
private BuildProperties buildProperties;

示例:单元测试验证

@Test
void givenBuildPropertiesBean_WhenFetchDefaultBuildProperties_ThenGetValidValues() {
    Assertions.assertEquals("spring-boot-properties", buildProperties.getArtifact());
    Assertions.assertEquals("com.baeldung.spring-boot-modules", buildProperties.getGroup());
    Assertions.assertEquals("0.0.1-SNAPSHOT", buildProperties.getVersion());
}

获取自定义属性:

@Test
void givenBuildPropertiesBean_WhenFetchCustomBuildProprties_ThenGetValidValues() {
    Assertions.assertEquals("123", buildProperties.get("custom.value"));
    Assertions.assertNotNull(buildProperties.get("java.version"));
    Assertions.assertEquals("Spring Boot Properties Module", buildProperties.get("description"));
}

⚠️ 注意:自定义字段通过 get("key") 访问,返回 String 类型。

5.3 Gradle 配置

Gradle 用户只需在 build.gradle 中添加:

springBoot {
    buildInfo {
        properties {
            additional = [
                'description': project.getDescription(),
                'java.version': JavaVersion.current(),
                'custom.value': 123,
            ]
        }
    }
}

构建后,文件会生成在:

build/resources/main/META-INF/build-info.properties

内容与 Maven 版本一致。✅ 应用代码无需任何改动,Spring Boot 自动识别并加载。


6. 总结

方法 适用场景 推荐度
@xxx@ + resource filtering 快速注入配置文件 ⭐⭐⭐⭐
更换 delimiter(如 ^xxx^ 使用 YAML 时必备 ⭐⭐⭐⭐
BuildProperties Bean 代码中动态读取构建信息 ⭐⭐⭐⭐⭐

最佳实践建议

  • 日常项目优先使用 spring-boot-starter-parent,省去大量配置。
  • 展示类信息(如版本页)使用 BuildProperties,灵活且类型安全。
  • 构建时间、JVM 版本等运维信息,通过 additionalProperties 注入,便于问题排查。
  • 避免在测试资源中依赖构建属性,容易踩坑。

通过合理利用 Spring Boot 的构建集成能力,可以轻松实现“一次定义,处处可用”,彻底告别硬编码。


原始标题:Spring Boot and Build Properties | Baeldung