文章
问答
冒泡
JAVA编译自动生成项目version及build号

        在项目的开发中,有时候我们需要给程序制定一个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

java
springboot
version

关于作者

Dane.shang
快30岁了还没去过酒吧
获得点赞
文章被阅读