Maven 构建产物添加时间戳:maven.build.timestamp、build-helper 与可复现构建说明
前言
在 Maven 项目发布或手工交付时,有时希望构建产物文件名带上时间戳,例如:
order-service-1.0.0-20260629173000.jar
这样做的好处是:
- 方便区分不同构建批次;
- 便于排查线上部署的是哪一次构建;
- 临时交付、测试环境部署时更直观;
- 可以和 CI/CD 构建号、Git Commit ID 一起提升可追踪性。
但也要注意:时间戳构建和可复现构建存在天然冲突。如果每次构建产物名和归档内容都带当前时间,那么同一份源码重复构建就不容易得到完全一致的结果。
方案一:使用 Maven 内置 maven.build.timestamp
Maven 内置了 ${maven.build.timestamp} 属性,表示构建开始时间。
可以在 pom.xml 中设置时间格式:
<properties>
<maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format>
</properties>
然后在 build.finalName 中使用:
<build>
<finalName>${project.artifactId}-${project.version}-${maven.build.timestamp}</finalName>
</build>
构建后产物名类似:
order-service-1.0.0-20260629173000.jar
需要注意:Maven 官方文档说明 ${maven.build.timestamp} 使用的是 UTC 时间。因此如果你的服务器位于中国时区,看到的时间可能和本地时间相差 8 小时。
方案二:使用 build-helper-maven-plugin 指定时区
如果希望生成指定时区的时间戳,可以使用 build-helper-maven-plugin 的 timestamp-property 目标。
示例:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>timestamp-property</id>
<phase>validate</phase>
<goals>
<goal>timestamp-property</goal>
</goals>
<configuration>
<name>build.time</name>
<pattern>yyyyMMddHHmmss</pattern>
<timeZone>Asia/Shanghai</timeZone>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<finalName>${project.artifactId}-${project.version}-${build.time}</finalName>
</build>
这里要注意变量名要一致:
<name>build.time</name>
对应使用:
${build.time}
如果写成 <name>current.time</name>,后面就应该使用 ${current.time}。变量名不一致会导致 finalName 中取不到期望的值。
方案三:使用 buildnumber-maven-plugin
buildnumber-maven-plugin 也可以生成时间戳或构建编号。
示例:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>create-timestamp</goal>
</goals>
</execution>
</executions>
<configuration>
<timestampFormat>yyyyMMddHHmmss</timestampFormat>
<timestampPropertyName>buildTimestamp</timestampPropertyName>
</configuration>
</plugin>
然后使用:
<build>
<finalName>${project.artifactId}-${project.version}-${buildTimestamp}</finalName>
</build>
注意插件名是 buildnumber-maven-plugin,不是 buildnubmer-maven-plugin。
方案四:使用 CI/CD 构建号或 Git Commit ID
在正式发布流程中,比单纯时间戳更推荐的是:
版本号 + 构建号 + Git Commit ID
例如:
order-service-1.0.0-build.1024-a1b2c3d.jar
CI/CD 中可以通过环境变量传入:
mvn clean package \
-Dbuild.number=${BUILD_NUMBER} \
-Dgit.commit=${GIT_COMMIT}
POM 中使用:
<build>
<finalName>${project.artifactId}-${project.version}-build.${build.number}-${git.commit}</finalName>
</build>
这种方式更适合 Jenkins、GitLab CI、GitHub Actions 等流水线,因为构建号和 Commit ID 比本地时间更容易追踪源码版本。
如果你在使用 Jenkins / GitLab 做发布,可以参考:Jenkins 基于 GitLab 分支与 Tag 的自动化打包发布实践。
finalName 只影响产物文件名
finalName 只影响 target 目录下生成的文件名,不等同于 Maven 坐标版本。
例如:
<groupId>com.example</groupId>
<artifactId>order-service</artifactId>
<version>1.0.0</version>
即使你设置:
<finalName>order-service-1.0.0-20260629173000</finalName>
Maven 坐标仍然是:
com.example:order-service:1.0.0
如果发布到 Maven 仓库,真正用于依赖解析的是 groupId、artifactId、version,而不是文件名里的时间戳。
时间戳与可复现构建的取舍
Maven 支持可复现构建,常用属性是:
<properties>
<project.build.outputTimestamp>2026-06-29T17:30:00Z</project.build.outputTimestamp>
</properties>
它用于控制归档文件中的输出时间戳,帮助同一份源码在相同环境下构建出可重复的产物。
因此需要区分两件事:
| 目标 | 推荐做法 |
|---|---|
| 临时交付、测试环境区分包 | 可以在 finalName 中加时间戳。 |
| 正式发布、Maven 仓库依赖 | 优先使用语义化版本、构建号、Git Commit ID。 |
| 追求可复现构建 | 避免使用当前时间作为产物内容的一部分,使用固定 outputTimestamp。 |
完整示例
下面是一个使用 build-helper-maven-plugin 生成上海时区时间戳的示例:
<project>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<build>
<finalName>${project.artifactId}-${project.version}-${build.time}</finalName>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>timestamp-property</id>
<phase>validate</phase>
<goals>
<goal>timestamp-property</goal>
</goals>
<configuration>
<name>build.time</name>
<pattern>yyyyMMddHHmmss</pattern>
<timeZone>Asia/Shanghai</timeZone>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
执行:
mvn clean package
产物示例:
target/order-service-1.0.0-20260629173000.jar
常见问题
1. 为什么时间少了 8 小时?
如果使用 ${maven.build.timestamp},它是 UTC 时间。需要本地时区时,可以使用 build-helper-maven-plugin 设置 timeZone。
2. 为什么 ${timestamp} 没有值?
检查插件生成的属性名是否叫 timestamp。如果配置的是:
<name>build.time</name>
就应该使用:
${build.time}
3. 是否应该把时间戳写进 version?
一般不建议。version 是 Maven 坐标的一部分,会影响依赖解析和仓库管理。时间戳更适合用于 finalName、CI 构建元数据或发布说明。
小结
Maven 构建产物加时间戳有三种常见方式:
- 简单场景使用
${maven.build.timestamp}; - 需要指定时区时使用
build-helper-maven-plugin; - 正式 CI/CD 发布优先使用构建号和 Git Commit ID。
如果目标是可复现构建,则要谨慎使用当前时间,避免每次构建都产生不可重复的产物。