Lombok实现方式JSR-269

前言

简介

Lombok是一款好用顺手的工具,就像Google Guava一样,在此予以强烈推荐,每一个Java工程师都应该使用它。Lombok是一种Java™实用工具,可用来帮助开发人员消除Java的冗长代码,尤其是对于简单的Java对象(POJO)。它通过注释实现这一目的。通过在开发环境中实现Lombok,开发人员可以节省构建诸如hashCode()和equals()这样的方法以及以往用来分类各种accessor和mutator的大量时间。

Lombok的实现方式是什么呢?
新建一个测试类使用Lombok的Getter和Setter注解,通过IDEA进行编译

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class UserInfo {
    private String userId;

    private String userName;
}

打开编译后生成的UserInfo.class文件

发现已经生成了get、set方法,由此可以推断出Lombok是在编译期为代码进行了增强,那么在编译期进行增强是如何实现的?

编译阶段

在JDK6提出并通过了JSR-269提案,提案通过了一组被称为“插入式注解处理器”的标准API,可以提前至编译期对代码中的特定注解进行处理, 从而影响到编译器的工作过程。
对于底层的一些实现,普遍会认为实现是像虚拟机一样使用C++实现,对于Java程序员来说并不是特别友好。但是Javac编译器是使用Java实现的更容易上手。
Javac的编译过程大致分为几步

  1. 准备过程:初始化插入式注解处理器
  2. 解析与填充符号表过程 词法、语法分析构建抽象语法树(AST)
  3. 插入式注解处理器的注解处理过程
  4. 分析与字节码生成过程
  5. 语法树变动后会再次解析与填充符号表,语法树没有变动时编译器就不会再对源码字符流操作,而是基于抽象语法树

综上所述想实现Lombok的效果只需要遵守JSR-269在编译期对AST进行操作即可实现
当然不止有Lombok通过这种方式实现,例如FindBug、MapStruct等也通过这种方式实现

实现

JCTree

JCTree是AST元素的基类,想实现效果只需要添加JCTree节点即可
JCTree是一个抽象类部分实现

从类名可以猜到是什么节点使用的这里挑几个常用的解释
JCStatement 声明语法树节点
JCBlock 语法块
JCReturn:return语句语法树节点
JCClassDecl:类定义语法树节点
JCVariableDecl:字段/变量定义语法树节点
JCMethodDecl:方法定义语法树节点
JCModifiers:访问标志语法树节点
JCExpression:表达式语法树节点,常见的子类如下
JCAssign:赋值语句语法树节点
JCIdent:标识符语法树节点 例如this

TreeMaker

主要用于生成语法树节点

代码

首先需要注解类,标明作用的范围和作用的时期,两个类分别对应Lombok的Getter、Setter

@Target({ElementType.TYPE}) //加在类上的注解
@Retention(RetentionPolicy.SOURCE) //作用于编译期
public @interface Getter {
}
@Target({ElementType.TYPE}) //加在类上的注解
@Retention(RetentionPolicy.SOURCE) //作用于编译期
public @interface Setter {
}

新建抽象注解处理器需要继承AbstractProcessor,这里使用模板方法模式

public abstract class MyAbstractProcessor extends AbstractProcessor {
    //语法树
    protected JavacTrees trees;

    //构建语法树节点
    protected TreeMaker treeMaker;

    //创建标识符的对象
    protected Names names;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //获取注解标识的类
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(getAnnotation());
        //拿到语法树
        set.stream().map(element -> trees.getTree(element)).forEach(jcTree -> jcTree.accept(new TreeTranslator() {
            //拿到类定义
            @Override
            public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
                //拿到所有成员变量
                for (JCTree tree : jcClassDecl.defs) {
                    if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
                        JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
                        jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
                    }
                }

                jcVariableDeclList.forEach(jcVariableDecl -> {
                    jcClassDecl.defs = jcClassDecl.defs.prepend(makeMethodDecl(jcVariableDecl));
                });
                super.visitClassDef(jcClassDecl);
            }
        }));
        return true;
    }

    /**
     * 创建方法
     * @param jcVariableDecl
     * @return
     */
    public abstract JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl);

    /**
     * 获取何种注解
     * @return
     */
    public abstract Class<? extends Annotation> getAnnotation();
}

用于处理Setter注解 继承MyAbstractProcessor

@SupportedAnnotationTypes("com.ingxx.processor.Setter") //注解处理器作用于哪个注解 也可以重写getSupportedAnnotationTypes
@SupportedSourceVersion(SourceVersion.RELEASE_8) //可以处理什么版本 也可以重写getSupportedSourceVersion
public class SetterProcessor extends MyAbstractProcessor {

    @Override
    public JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        //生成函数体 this.name = name;
        statements.append(treeMaker.Exec(treeMaker.Assign(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName()),treeMaker.Ident(jcVariableDecl.getName()))));
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
        //生成方法
        return treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC), //访问标志
                getNewMethodName(jcVariableDecl.getName()), //名字
                treeMaker.TypeIdent(TypeTag.VOID), //返回类型
                List.nil(), //泛型形参列表
                List.of(getParameters(jcVariableDecl)), //参数列表
                List.nil(), //异常列表
                body, //方法体
                null //默认方法(可能是interface中的那个default)
        );
    }

    @Override
    public Class<? extends Annotation> getAnnotation() {
        return Setter.class;
    }

    private Name getNewMethodName(Name name) {
        String fieldName = name.toString();
        return names.fromString("set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1, name.length()));
    }

    private JCTree.JCVariableDecl getParameters(JCTree.JCVariableDecl prototypeJCVariable) {
        return treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PARAMETER), //访问标志
                prototypeJCVariable.name, //名字
                prototypeJCVariable.vartype, //类型
                null //初始化语句
        );
    }
}

用于处理Getter注解 继承MyAbstractProcessor

@SupportedAnnotationTypes("com.ingxx.processor.Getter") //注解处理器作用于哪个注解 也可以重写getSupportedAnnotationTypes
@SupportedSourceVersion(SourceVersion.RELEASE_8) //可以处理什么版本 也可以重写getSupportedSourceVersion
public class GetterProcessor extends MyAbstractProcessor {

    @Override
    public JCTree.JCMethodDecl makeMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        //生成函数体 return this.字段名
        statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName())));
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
        //生成方法
        return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), getNewMethodName(jcVariableDecl.getName()), jcVariableDecl.vartype, List.nil(), List.nil(), List.nil(), body, null);
    }

    @Override
    public Class<? extends Annotation> getAnnotation() {
        return Getter.class;
    }

    private Name getNewMethodName(Name name) {
        String fieldName = name.toString();
        return names.fromString("get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1, name.length()));
    }
}

编译现有类

javac -cp $JAVA_HOME/lib/tools.jar *.java -d . 

新建一个测试类 IDEA中由于找不到get set方法会报错,可以忽略

@Getter
@Setter
public class UserInfo {
    private String userId;

    private String userName;

    public static void main(String[] args) {
        UserInfo userInfo = new UserInfo();
        userInfo.setUserId("001");
        userInfo.setUserName("得物");
        System.out.println("id = "+userInfo.getUserId()+" name = "+userInfo.getUserName());
    }
}

接着编译

//多个处理器用逗号分隔
javac -processor com.ingxx.processor.GetterProcessor,com.ingxx.processor.SetterProcessor UserInfo.java -d .

查看编译后的文件发现已经生成了get、set方法

以上就是从Lombok到JSR-269的详细内容,更多关于JS 反射机制的资料请关注我们其它相关文章!

(0)

相关推荐

  • IDEA安装lombok插件设置Enable Annotation Processing后编译依然报错解决方法

    IDEA导入的项目中有依赖lombok的get set注解,build项目时报错:找不到get/set方法. 查找网上资料,安装lombok插件,如图: 安装好插件后,重启IDEA后还是编译报错,又在设置中勾选了Enable Annotation Processing 如下图: 完成了所有这些设置后编译还是报错找不到get/set方法 经过了很多次尝试后 (更换JDK1.8.清空项目缓存等等)发现项目中引用的lombok jar包是1.16版本的,下载了官网的最新jar包并替换后重新编译,编译通

  • IDEA下lombok安装及找不到get,set的问题的解决方法

    今天尝试在IDEA中使用Lombok,但是在编译时,提示找不到set()和get()方法,我明明在javabean中使用了@Data注解,但是编译器就是找不到.于是从网上查询了很多的方法去解决,最后终于解决了.接下来我就将过程分享一下,希望能够帮助需要的人: Idea下安装lombok(需要二步) 第一步: pom.xml中加入lombok依赖包 <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <d

  • 解决IDEA2020.2插件lombok报错问题(亲测有效)

    先看看问题是否和我一样 由于我的修复好了,所以我的没错误了,我指出我以前的错误看是否匹配 当你只写一个注解的时候是好的,有时候写第一个注解的时候就崩了,第二个注解就更不用说了 是不是下面这个红框框的标志都不见了?并且你的右下角也报错了 如果是可以直接下载这个非官方补丁: 链接:https://pan.baidu.com/s/1QvDWMN3aihHHus9f1pVPFQ 提取码:we6s 下载下来后是直接是这个样子的 因为我安装的是解压后的,所以我发的是解压后的,如果安装有问题可以在评论区告诉我

  • 解决IDEA2020.1版本不兼容Lombok的问题

    Lombok不兼容: 问题:Plugin Error: Plugin "Lombok" is incompatible 解决办法: 1.进入Lombok官网插件地址下载0.29-EAP版本 2.打开Settings -->Plugins 3.选择刚刚下载好的zip包 4.重启IDEA就搞定啦 IDEA2020.1更新了什么? 到此这篇关于解决IDEA2020.1版本不兼容Lombok的问题的文章就介绍到这了,更多相关IDEA2020.1不兼容Lombok内容请搜索我们以前的文章或

  • IDEA下因Lombok插件产生的Library source does not match the bytecode报错问题及解决方法(亲测可用)

    写项目的时候 遇到了个这个问题:Library source does not match the bytecode XXX 然后自己找了找 大部分的解决方法都是没有解决方法 越看心越凉 部分解决方案是清空缓存 试了下并没有什么用 还有些解决方案是用Gradle管理的 而我是用Maven管理的 不太适用 最后然后自己摸索着解决了 由于该问题我已经解决了 无法复现 因而无法截图 就用文字来描述吧: 1.卸载IDEA中的Lombok插件 File-Settings-Plugins 搜索Lombok

  • 详解Idea 2019.2 安装lombok插件失效问题解决

    一.lombok简介 lombok 提供了使用注解的形式帮助简化消除java代码.在编写Java代码时,通过使用对应的注解,可以简化开发,同时,在编译源码的时候,lombok又自动生成对应的代码.所以,使用lombok插件不会影响程序的运行效率. 二.lombok常用注解 @Data :注解在类上:包含了@ToString,@EqualsAndHashCode,@Getter / @Setter和@RequiredArgsConstructor的功能,提供类所有属性的 getter 和 sett

  • Lombok实现方式JSR-269

    前言 简介 Lombok是一款好用顺手的工具,就像Google Guava一样,在此予以强烈推荐,每一个Java工程师都应该使用它.Lombok是一种Java™实用工具,可用来帮助开发人员消除Java的冗长代码,尤其是对于简单的Java对象(POJO).它通过注释实现这一目的.通过在开发环境中实现Lombok,开发人员可以节省构建诸如hashCode()和equals()这样的方法以及以往用来分类各种accessor和mutator的大量时间. Lombok的实现方式是什么呢? 新建一个测试类使

  • 解决Lombok注解不起作用的问题

    Lombok注解不起作用 场景: 减少实体类中如Getter,Setter方法的书写 原因: lombok是一个第三方插件,我们使用时需要进行两个步骤(两个步骤缺一不可): 1:引入lombok依赖 2: 安装lombok插件 作用: Lombok是一个第三方插件,通过它我们可以直接书写注解来代替原来的getter,setter,toString等方法. 深入理解Lombok 说道Lombok,可能会鲜为人知.但是在实际的开发中,它起到了很大的作用,话不多说,直入正题: 一.Lombok是什么

  • Java效率工具之Lombok的具体使用

    还在编写无聊枯燥又难以维护的POJO吗?洁癖者的春天在哪里?请看Lombok! 在过往的Java项目中,充斥着太多不友好的代码:POJO的getter/setter/toString:异常处理:I/O流的关闭操作等等,这些样板代码既没有技术含量,又影响着代码的美观,Lombok应运而生. 首先说明一下:任何技术的出现都是为了解决某一类问题的,如果在此基础上再建立奇技淫巧,不如回归Java本身.应该保持合理使用而不滥用. Lombok的使用非常简单,下面我们一起来看下: 1)引入相应的maven包

  • Java Lombok简介、使用、工作原理、优缺点

    简介 官方介绍 Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java. Never write another getter or equals method again, with one annotation your class has a fully featured builder, automate your lo

  • IDEA 2022 中的Lombok 使用基础教程

    目录 1.Lombok是什么 1.1 Lombok 是什么? 2.POM 中引入依赖 3.IDE 中安装插件 4. Lombok 使用 4.1 Lombok 使用注意 5.代码案例: 6. 常用注解 1.Lombok是什么 ​ Lombok是使用java编写的一款开源类库.其主作用是使用注解来代替一些具有格式固定,没有过多技术含量的编码工作.使我们可以仅仅在代码中添加一个注解(annotation),就可以起到和编写一大段代码一样的作用.对于这些格式固定的的代码,IDE也提供了自动生成的功能,但

  • Java使用lombok消除冗余代码的方法步骤

    一.项目背景 在写Java程序的时候经常会遇到如下情形: 新建了一个Class类,然后在其中设置了几个字段,最后还需要花费很多时间来建立getter和setter方法. lombok项目的产生就是为了省去我们手动创建getter和setter方法的麻烦,它能够在我们编译源码的时候自动帮我们生成getter和setter方法.即它最终能够达到的效果是:在源码中没有getter和setter方法,但是在编译生成的字节码文件中有getter和setter方法. 比如源码文件: import java.

  • 基于Lombok集成springboot遇到的坑

    目录 Lombok集成springboot遇到的坑 问题 原因 springboot引入Lombok Lombok集成springboot遇到的坑 最近有同事在spring boot中用Lombok @Data注解时遇到了一个奇怪的问题,然后有幸一起研究了一下,把研究成果记录下来. 问题 先上代码: @Data public abstract class TestAbstract { private RedisTemplate redisTemplate; public TestAbstract

  • Lombok和MapStruct整合详情

    目录 一.流程 1.安装Lombok插件 2.启用注解处理器 二.原理 三.原因 四.解决办法 一.流程 1.安装Lombok插件 (2020.0.4之后版本的IDEA已内置Lombok,老版本的请自行下载插件) 2.启用注解处理器 打开setting -> enable annotation processor (启用注解处理器) OK 完事~ 可以使用了  送上常用四件套: @Data //生成Getter .Setter .ToString .ToString .EqualsAndHash

  • Lombok同时使⽤@Data和@Builder踩坑总结

    目录 问题背景 Lombok @Data和@Builder分别单独分析用法 解决方法 Lombok原理 总结 问题背景 Lombok使⽤ 同时使⽤@Data和@Builder ,构建无参构造器报错!编译不通过.如下图: Lombok @Data和@Builder分别单独分析用法 Lombok使⽤@Data可以⽣成⽆参构造和类⾥⾯所有属性的getter/setter⽅法.可以简化我们代码的开发.(需要安装Lombok插件和引⼊Lombok依赖). 例如下⾯的⼀个实体类,引⼊Lombok后,可以⾃动

  • 提升java开发效率工具lombok使用争议

    目录 引言 什么是 lombok 如何使用 lombok 的原理和滥用 引言 对使用 lombok 还是有很多争议的,有些公司不建议使用,有些公司又大量使用. 我们的想法是:可以使用,但是不要滥用. 什么是 lombok Lombok是 一种Java™实用工具,可用来帮助开发人员消除Java的冗长代码,尤其是对于简单的Java对象(POJO) . 它通过注释实现这一目的. 通过在开发环境中实现Lombok,开发人员可以节省构建诸如hashCode() 和 equals()这样的方法以及以往用来分

随机推荐