在项目的开发中,有时候我们需要给程序制定一个version,这个version通常规范了当前版本程序的生命周期及功能点.除此之外我们通常还会给程序制定build号来确定小版本更新周期及发布日期.这里我做了两种可以项目发布自动更新version及build的方法.
方式一、
可以使用maven变量替换法来生成,此方法只适用于普通项目,大致的方向为创建一个properties文件, 通过maven插件的filtering来实现复制资源文件时,对资源文件的变量进行替换,然后再读取此文件产生版本号.
1、maven坐标
(1) parent (由于是做基础组件,所以需要一个parent项目做依赖管理及公共插件的配置)
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 时间格式化 -->
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
<!--maven.build.timestamp保存了maven编译时间戳-->
<timestamp>${maven.build.timestamp}</timestamp>
<version>${project.version}</version>
</properties>
<build>
<resources>
<resource>
<directory>src/main/resources/</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
2、组件模块继承parent项目
<parent>
<groupId>com.dane</groupId>
<artifactId>project-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath>../project-parent</relativePath>
</parent>
3、在项目的资源文件夹下新建maven-version.properties
(这里懒得创建多个项目,就直接在组件里创建了)
maven.package.time=@timestamp@
maven.package.version=@project.version@
maven的资源变量替换符号是@,这里我引用了上面的版本和编译时间的变量.
4、编译代码

这里可以看到target目录下已经有了版本号的文件,打开看看里面变量已经被替换.

注意: 这里maven获取的时间为UTC时间,和北京时间差8小时需要自行转换.
5、读取代码
(1)、properties文件读取
@Data
@Configuration
@ConfigurationProperties(prefix = "maven.package",ignoreUnknownFields = false)
@PropertySource(value = "classpath:/maven-version.properties",encoding = "utf-8")
public class MavenVersionProperties {
private String time;
private String version;
}
(2)、读取版本号及编译时间
@Slf4j
@Component
@RequiredArgsConstructor
public class MavenVersionBuild {
private final MavenVersionProperties mavenVersionProperties;
public void version(){
try {
String buildTime = formatUTC8Time(mavenVersionProperties.getTime());
log.info("this project mavenPlug build version by " + mavenVersionProperties.getVersion()+"-"+buildTime);
// TODO: 2023/4/4 save the project version to db
}catch (Exception e) {
e.printStackTrace();
}
}
public static String formatUTC8Time(String date){
if (date == null) {
return null;
}
Date oldDate=null;
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
oldDate = simpleDateFormat.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(oldDate);
calendar.add(Calendar.HOUR_OF_DAY, +8);
SimpleDateFormat sdf = new SimpleDateFormat("MMddHH");
return sdf.format(calendar.getTime());
}
}
(3)、测试调用(项目启动时获取编译版本号)
@Component
@RequiredArgsConstructor
public class VersionRunner implements CommandLineRunner {
private final MavenVersionBuild mavenVersionBuild;
@Override
public void run(String... args) throws Exception {
// String os = System.getProperty("os.name").toLowerCase();
// if (os.contains("mac") || os.contains("win")) {
// return;
// }
mavenVersionBuild.version();
}
}
可以看到, 打印出来项目版本号及build号.

这里我将build号定为当前月日时.
2023-04-04 15:03:43.399 INFO 5819 --- [ main] c.dane.version.manage.MavenVersionBuild : this project mavenPlug build version by 1.0-SNAPSHOT-040414
方式二、
使用git-commit-id-maven-plugin生成jar包版本,此方法优势点在于不用提前手动创建properties文件而是自动生成,更适合组件使用(多个组件);
1、maven坐标
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>4.9.9</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
<phase>initialize</phase>
</execution>
<execution>
<id>validate-the-git-infos</id>
<goals>
<goal>validateRevision</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<dateFormat>MMddHH</dateFormat>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>${project.build.outputDirectory}/version.properties
</generateGitPropertiesFilename>
<includeOnlyProperties>
<includeOnlyProperty>^git.build.(time|version)$</includeOnlyProperty>
</includeOnlyProperties>
<commitIdGenerationMode>full</commitIdGenerationMode>
</configuration>
</plugin>
这里使用这个组件, 会在compile的时候生成version.properties,也可自定义文件名及生成文件夹地址(不会体现到未编译代码文件夹中).
<resources>
<resource>
<directory>src/main/resources/</directory>
<excludes>
<exclude>version.properties</exclude>
</excludes>
<filtering>true</filtering>
</resource>
</resources>
同时因为前面方法一使用变量替换法, 这里将version.properties排除.
2、编译

可以看到编译后的目录中出现了两个properties文件, 一个为方法一替换变量的,另一个则是自动生成的.

查看生成的版本信息, 没有错误.
3、读取生成版本信息
这里方法二的读取和方法一有些不同,由于是组件生成的properties,就会导致实际上所有依赖此parent的组件及项目都会生成一个版本信息(这个踩坑踩了很久),加上Spring读取同名配置的机制为先内后外覆盖, 就导致实际上真正外围的项目生成的版本反而读取不到,读取到的却是我提供的组件版本,也就是说使用常规方式读取到的prperties为错误的, 只能通过系统变量来获取所有的properties文件,再通过classpath进行比对,如果使用java -cp启动,还需要通过当前类的加载器反向加载启动类,再获取启动类所在jar包名称,然后根据名称在classpath中进行比对确定.
public class ThreadPoolConfigure {
@Bean(name = "versionScheduler")
public ThreadPoolTaskScheduler versionScheduler() {
return new TaskSchedulerBuilder()
.poolSize(1)
.threadNamePrefix("versionTask")
.build();
}
}
使用自定义线程池可以获取当前线程的类加载器.
@Slf4j
@Component
public class VersionBuild {
@Resource(name = "versionScheduler")
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
public void build(){
threadPoolTaskScheduler.execute(new Runnable() {
@Override
public void run() {
try {
Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources("version.properties");
String buildVersion = "";
// 尝试获取java -xx启动命令
String commandJarName = System.getProperty("sun.java.command");
// 尝试获取classpath启动jar包
String classPath = System.getProperty("java.class.path");
// 这里如果启动方式为java -cp ,则使用类加载器读取当前class所在的主jar包名称匹配
if (commandJarName.startsWith("com.")) {
// 使用类加载器加载主jar包名所对应的application lass
Class<?> aClass = Thread.currentThread().getContextClassLoader().loadClass(commandJarName);
if (aClass != null) {
// 获取application 所在jar包名称
commandJarName = aClass.getProtectionDomain().getCodeSource().getLocation().getFile();
}
}
// 遍历所有 系统遍历, 匹配启动类所在jar包的 版本信息
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
String path = url.getPath();
if ((commandJarName != null && path.contains(commandJarName)) ||
(classPath != null && path.contains(classPath))) {
InputStream inputStream = url.openStream();
try {
Properties properties = new Properties();
properties.load(inputStream);
String version = properties.getProperty("git.build.version");
String buildNum = properties.getProperty("git.build.time");
if (version != null) {
buildVersion = version + "-";
}
if (buildNum != null) {
buildVersion = buildVersion + buildNum;
break;
}
}catch (Exception e) {
e.printStackTrace();
}finally {
inputStream.close();
}
}
}
log.info("this project build version by "+ buildVersion);
// TODO: 2023/4/4 save the project version to db
}catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
4、查看结果(这次两种方法都打印)
@Component
@RequiredArgsConstructor
public class VersionRunner implements CommandLineRunner {
private final MavenVersionBuild mavenVersionBuild;
private final VersionBuild versionBuild;
@Override
public void run(String... args) throws Exception {
// String os = System.getProperty("os.name").toLowerCase();
// if (os.contains("mac") || os.contains("win")) {
// return;
// }
mavenVersionBuild.version();
versionBuild.build();
}
}
2023-04-04 15:28:49.906 INFO 6144 --- [ main] c.dane.version.manage.MavenVersionBuild : this project mavenPlug build version by 1.0-SNAPSHOT-040415
2023-04-04 15:28:49.911 INFO 6144 --- [ versionTask1] com.dane.version.manage.VersionBuild : this project build version by 1.0-SNAPSHOT-040415