项目终于用上了插入式注解,真香!

科技   2024-11-13 11:31   山西  

说实话,作为程序员,总是逃不了和版本号打交道的命运。

每次发布新版本,都要手动修改项目中的版本号,弄得我心情都变得特别复杂。尤其是每次版本迭代之后,多个模块的版本号必须同步更新。

要知道,我们公司有很多小模块,每个模块都要更新,而且不能出错!有时就算多加个"1",也会变成"110",你说这不挺烦人的嘛。

于是我决定,这个问题得用点技术手段来解决。

最后,通过引入插入式注解处理器,这个难题迎刃而解了。今天就来给大家详细介绍一下这个过程,如何利用注解自动注入版本号,少做重复工作,少写一点代码,简直太爽了!🌟


# 背景与需求


首先,问题的根本是:我们有多个Java模块,每次版本迭代时,我们希望自动注入当前的JAR版本号。这是一个相对简单但很繁琐的需求,如果不处理好,版本号的更新就可能变成整个项目的痛点,尤其是当涉及到多个模块时。

每次你得手动去改每个模块的版本号,这样就很麻烦,代码也不够干净。

为了解决这个问题,我们想到了一个办法:通过类似Lombok的注解方式,在编译时自动为每个需要注入版本号的地方加上对应的版本信息。说白了,谁能想到用注解来解决这些问题呢?


# 问题描述


现在的痛点就是:每次版本迭代时,我们都得手动修改每个模块的版本号,特别是当模块数量多的时候,手动更新版本号成了一件相当冗余且容易出错的事情。

像Lombok这种通过注解生成代码的方式,一直让我很感兴趣,想想如果也能通过注解来自动给字段注入版本号,那岂不是太棒了?

就这样,我想着,如果能搞个注解处理器,在编译时自动修改源代码并注入版本号,那就能大大节省人力物力,还能避免各种小错误。


# 解决方案


于是,经过一番思考,我的解决方案就是:自定义注解注解处理器

通过创建一个类似Lombok的注解,用来标记需要注入版本号的字段,注解处理器会在编译时自动为这些字段注入当前的版本号。

这样,我们不需要每次手动更新版本号,避免了重复劳动。


# 实现方案

1. 自定义注解:@TrisceliVersion

首先,我们定义一个简单的注解 @TrisceliVersion,它用于标记那些需要自动注入版本号的字段。这个注解其实就只是一个标记而已,里面啥都不写,只是为了让注解处理器知道哪些字段需要注入版本号。

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)  // 表示此注解只能用在字段上
@Retention(RetentionPolicy.SOURCE)  // 表示注解只在源码阶段有效
public @interface TrisceliVersion {
}

2. 编写注解处理器

接下来,写一个注解处理器,负责在编译时为标记了 @TrisceliVersion 的字段注入版本号。为了实现这个功能,我们需要继承 AbstractProcessor 类,并重写 process 方法。

注解处理器的主要工作是:读取项目的版本号,然后修改抽象语法树(AST),为标记了 @TrisceliVersion 的字段注入版本号。

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.util.Set;

public class VersionAnnotationProcessor extends AbstractProcessor {

    private Elements elementUtils;
    private Types typeUtils;

    @Override
    public void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        elementUtils = processingEnv.getElementUtils();
        typeUtils = processingEnv.getTypeUtils();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Set.of(TrisceliVersion.class.getCanonicalName());
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (Element element : roundEnv.getElementsAnnotatedWith(TrisceliVersion.class)) {
            // 获取版本号(可以从Maven或者Gradle配置中获取)
            String version = getProjectVersion();
            
            // 通过注解处理器修改AST(实际操作通常涉及到代码生成或修改)
            // 这里只是模拟代码注入,实际使用时可以通过注解处理器生成字节码或者修改源代码
            System.out.println("Injecting version: " + version + " into field: " + element.getSimpleName());
        }
        return true;
    }

    private String getProjectVersion() {
        // 这里可以从pom.xml或gradle.properties文件中读取版本号
        return "1.0.0";  // 假设版本号是1.0.0
    }
}

3. 通过Gradle或Maven构建

将注解处理器集成到项目的构建工具中,通常是通过Gradle或Maven进行构建,确保编译时自动生成包含版本号的字节码或源代码。


# 注解处理器工作原理


上面我们介绍了如何创建注解和注解处理器,接下来说下注解处理器的工作原理。

  • 编译期生效:注解处理器会在编译期处理源代码,它会遍历所有标记了 @TrisceliVersion 注解的字段,并注入版本号。
  • 修改AST:通过 AbstractProcessor 提供的 API,我们可以修改抽象语法树(AST),给字段注入版本号,或者生成新的代码。这些操作都发生在编译期间,所以不会影响运行时性能。

优点:

  • 自动化:最大的优点就是不需要手动修改版本号,减少了人为的错误和工作量。每次版本迭代时,只要构建项目,版本号就会自动注入。
  • 提高开发效率:通过注解来注入数据,避免了重复劳动,也让代码更加整洁。

缺点:

  • 依赖编译期:插入式注解处理器只能在编译时生效,不能在运行时动态注入版本号。如果项目要求动态注入版本号,可能就不适用。
  • 开发复杂度:引入注解处理器可能会增加一些开发复杂度,尤其是当项目中有多个注解处理器时,调试起来会有点麻烦。

# 总结

通过这次的注解处理器实践,我们在项目中成功实现了自动注入版本号的需求,不仅解决了手动更新版本号的痛点,也让代码变得更加优雅。

最重要的是,代码的维护变得更加高效,不需要每次手动去修改版本号了。

如果你的项目中也有类似的痛点,不妨试试这个方法,真的非常香!🔥


对编程、职场感兴趣的同学,可以链接我,微信:coder301 拉你进入“程序员交流群”。
🔥东哥私藏精品 热门推荐🔥

东哥作为一名超级老码农,整理了全网最全《Java高级架构师资料合集》

资料包含了《IDEA视频教程》《最全Java面试题库》、最全项目实战源码及视频》及《毕业设计系统源码》总量高达 650GB 。全部免费领取!全面满足各个阶段程序员的学习需求。

Java面试那些事儿
回复 java ,领取Java面试题。分享AI编程,Java教程,Java面试辅导,Java编程视频,Java下载,Java技术栈,AI工具,Java开源项目,Java简历模板,Java招聘,Java实战,Java面试经验,IDEA教程。
 最新文章