当前位置: 首页 > java >正文

java 插入式注解的打开方式!

插入式注解

     插入式注解处理器在《深入理解Java虚拟机》一书中有一些介绍,但一直没有机会使用,就简单用了一下,这里做个记录 。

     了解过lombok底层原理的都知道其使用的就是的插入式注解,那么今天就以真实场景演示一下插入式注解的使用。

02

最终效果

 比如我们可以定义一个空常量,加上自定义的注解:

@TrisceliVersionpublic static final String version = "";

 然后像lombok生成set/get方法那样注入真正的版本号:

@TrisceliVersionpublic static final String version = "1.0.31-SNAPSHOT";

java中解析一个注解的方式主要有两种:编译期扫描、运行期反射,这是lombok @Setter的实现:

@Target({ElementType.FIELD, ElementType.TYPE})@Retention(RetentionPolicy.SOURCE)public @interface Setter {    // 略...}

可以看到@SetterRetentionSOURCE类型的,也就是说这个注解只在编译期有效,它甚至不会被编入class文件,所以lombok无疑是第一种解析方式,那用什么方式可以在编译期就让注解被解析到并执行我们的解析代码呢?答案就是定义插入式注解处理器(通过JSR-269提案定义的Pluggable Annotation Processing API实现)

也就是说插入式注解处理器可以帮助我们在编译期修改抽象语法树(AST)!所以现在我们只需要自定义一个这样的处理器,然后其内部拿到jar版本信息(因为是编译期,可以找到源码的path,源码里随便搞个文件存放版本号,然后用java io读取进来即可),再将注解对应语法树上的常量值设置成jar包版本号,语法树变了,最终生成的字节码也会跟着变,这样就实现了我们想在编译期给常量version注入值的愿望。

03

代码实现

 自定义一个插入式注解处理器也很简单,首先要将自己的注解定义出来:

@Documented@Retention(RetentionPolicy.SOURCE) //只在编译期有效,最终不会打进class文件中@Target({ElementType.FIELD}) //仅允许作用于类属性之上public @interface TrisceliVersion {}

然后定义一个继承了AbstractProcessor的处理器:

/** * {@link AbstractProcessor} 就属于 Pluggable Annotation Processing API */public class TrisceliVersionProcessor extends AbstractProcessor {
    private JavacTrees javacTrees;    private TreeMaker treeMaker;    private ProcessingEnvironment processingEnv;
    /**     * 初始化处理器     *     * @param processingEnv 提供了一系列的实用工具     */    @SneakyThrows    @Override    public synchronized void init(ProcessingEnvironment processingEnv) {        super.init(processingEnv);        this.processingEnv = processingEnv;        this.javacTrees = JavacTrees.instance(processingEnv);        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();        this.treeMaker = TreeMaker.instance(context);    }    @Override    public SourceVersion getSupportedSourceVersion() {        return SourceVersion.latest();    }
    @Override    public Set<String> getSupportedAnnotationTypes() {        HashSet<String> set = new HashSet<>();        set.add(TrisceliVersion.class.getName()); // 支持解析的注解        return set;    }
    @Override    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {        for (TypeElement t : annotations) {            for (Element e : roundEnv.getElementsAnnotatedWith(t)) { // 获取到给定注解的element(element可以是一个类、方法、包等)                // JCVariableDecl为字段/变量定义语法树节点                JCTree.JCVariableDecl jcv = (JCTree.JCVariableDecl) javacTrees.getTree(e);                String varType = jcv.vartype.type.toString();                if (!"java.lang.String".equals(varType)) { // 限定变量类型必须是String类型,否则抛异常                    printErrorMessage(e, "Type '" + varType + "'" + " is not support.");                }                jcv.init = treeMaker.Literal(getVersion()); // 给这个字段赋值,也就是getVersion的返回值            }        }        return true;    }
    /**     * 利用processingEnv内的Messager对象输出一些日志     *     * @param e element     * @param m error message     */    private void printErrorMessage(Element e, String m) {        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, m, e);    }
    private String getVersion() {        /**         * 获取version,这里省略掉复杂的代码,直接返回固定值         */        return "v1.0.1";    }

定义好的处理器需要SPI机制被发现,所以需要定义META.services

04

测试

直接进行项目编译即可,看你的项目用的是 maven 或者 gradle。如下为编译后效果

http://www.xdnf.cn/news/17616.html

相关文章:

  • MySQL,Redis重点面试题
  • SQL179 每个6/7级用户活跃情况
  • Spring Framework源码解析——BeanPostProcessor
  • 【学习嵌入式day-22-Linux软件编程-IO】
  • SpringBoot集成支付宝二维码支付接口详解
  • Python3.10 + Firecrawl 下载 Markdown 文档:构建高效通用文章爬虫
  • 不同FPGA开发板系统移植步骤
  • Chrome插件开发【Service Worker练手小项目】
  • 【LeetCode刷题集】--排序(三)
  • 【智能的起源】人类如何模仿,简单的“刺激-反应”机制 智能的核心不是记忆,而是发现规律并能迁移到新场景。 最原始的智能:没有思考,只有简单条件反射
  • Mamba 原理汇总2
  • AI(2)-神经网络(激活函数)
  • 支持小语种的在线客服系统,自动翻译双方语言,适合对接跨境海外客户
  • 数据结构-数组扩容
  • 开发指南130-实体类的主键生成策略
  • Apache ECharts 6 核心技术解密 – Vue3企业级可视化实战指南
  • 排错000
  • 基于 ZooKeeper 的分布式锁实现原理是什么?
  • windows上RabbitMQ 启动时报错:发生系统错误 1067。 进程意外终止。
  • 150V降压芯片DCDC150V100V80V降压12V5V1.5A车载仪表恒压驱动H6203L惠洋科技
  • git:分支
  • 提示词工程实战:用角色扮演让AI输出更专业、更精准的内容
  • 软件测评中HTTP 安全头的配置与测试规范
  • 数据变而界面僵:Vue/React/Angular渲染失效解析与修复指南
  • 基于 Axios 的 HTTP 请求封装文件解析
  • Console Variables Editor插件使用
  • 音视频学习(五十三):音频重采样
  • QT QProcess + xcopy 实现文件拷贝
  • Web安全自动化测试实战指南:Python与Selenium在验证码处理中的应用
  • Mybatis @Param参数传递说明