注解 之 Lombok

本文介绍了如何使用Java的JSR269规范创建自定义注解处理器,模仿Lombok实现自动为类生成getter和setter方法。通过创建`@Getter`和`@Setter`注解,并编写对应的注解处理器,可以在编译期间动态插入getter和setter方法到源码中,简化代码编写。示例展示了如何处理类中的变量,生成getter和setter方法的逻辑,并给出了测试用例。
摘要由CSDN通过智能技术生成

lombok,get,set

1.目录树

 

2.创建Getter、Setter注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
    public @interface Getter {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
    public @interface Setter {
}

3.创建Getter、Setter对应的注解处理器

import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.*;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;

//对Getter感兴趣
@SupportedAnnotationTypes("com.hinotoyk.jsr269.Getter")
//支持的版本,使用1.8就写这个
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class GetterProcessor extends AbstractProcessor {

    // 编译时期输入日志的
    private Messager messager;

    // 将Element转换为JCTree的工具,提供了待处理的抽象语法树
    private JavacTrees trees;

    // 封装了创建AST节点的一些方法
    private TreeMaker treeMaker;

    // 提供了创建标识符的方法
    private Names names;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.messager = processingEnv.getMessager();
        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) {

        // 获取被@Getter注解标记的所有元素(这个元素可能是类、变量、方法等等)
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Getter.class);

        set.forEach(element -> {
            // 将Element转换为JCTree
            JCTree jcTree = trees.getTree(element);
            jcTree.accept(new TreeTranslator() {
                /***
                 * JCTree.Visitor有很多方法,我们可以通过重写对应的方法,(从该方法的形参中)来获取到我们想要的信息:
                 * 如: 重写visitClassDef方法, 获取到类的信息;
                 *     重写visitMethodDef方法, 获取到方法的信息;
                 *     重写visitVarDef方法, 获取到变量的信息; 
                 * @param jcClassDecl
                 */
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {

                    //创建一个变量语法树节点的List
                    List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();

                    // 遍历defs,即是类定义的详细语句,包括字段、方法的定义等等
                    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 -> {
                        messager.printMessage(Diagnostic.Kind.NOTE, "get " + jcVariableDecl.getName() + " has been processed"); 
                        treeMaker.pos = jcVariableDecl.pos;
                        //类里的前面追加生成的Getter方法
                        jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
                    });
                    super.visitClassDef(jcClassDecl);
                }

            });
        });
        //我们有修改过AST,所以返回true
        return true;
    }

    private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
        /***
         * JCStatement:声明语法树节点,常见的子类如下
         * JCBlock:语句块语法树节点
         * JCReturn:return语句语法树节点
         * JCClassDecl:类定义语法树节点
         * JCVariableDecl:字段/变量定义语法树节点
         * JCMethodDecl:方法定义语法树节点
         * JCModifiers:访问标志语法树节点
         * JCExpression:表达式语法树节点,常见的子类如下
         * JCAssign:赋值语句语法树节点
         * JCIdent:标识符语法树节点,可以是变量,类型,关键字等等
         */
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        
        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),//mods:访问标志
                getNewMethodName(jcVariableDecl.getName()),//name:方法名
                jcVariableDecl.vartype,//restype:返回类型
                List.nil(),//typarams:泛型参数列表
                List.nil(),//params:参数列表
                List.nil(),//thrown:异常声明列表
                body,//方法体
                null);
    }

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

        Settter注解处理器和Getter的大同小异,只是生成set方法的步骤不太一样,详细可查看openjdk-7 TreeMaker 

import com.sun.source.tree.Tree;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.*;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;

@SupportedAnnotationTypes("com.hinotoyk.jsr269.Setter")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class SetterProcessor extends AbstractProcessor {

    private Messager messager;

    private JavacTrees trees;

    private TreeMaker treeMaker;

    private Names names;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.messager = processingEnv.getMessager();
        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(Setter.class);

        set.forEach(element -> {
            JCTree jcTree = trees.getTree(element);
            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 -> {
                        messager.printMessage(Diagnostic.Kind.NOTE, "set " + jcVariableDecl.getName() + " has been processed");
                        treeMaker.pos = jcVariableDecl.pos;
                        jcClassDecl.defs = jcClassDecl.defs.prepend(makeSetterMethodDecl(jcVariableDecl));

                    });
                    super.visitClassDef(jcClassDecl);
                }

            });
        });
        return true;
    }


    private JCTree.JCMethodDecl makeSetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        statements.append(
                treeMaker.Exec(
                        treeMaker.Assign(
                                treeMaker.Select(
                                     treeMaker.Ident(names.fromString("this")),
                                     names.fromString(jcVariableDecl.name.toString())
                                ),
                                treeMaker.Ident(names.fromString(jcVariableDecl.name.toString()))
                        )
                )
        );

        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());

        // 生成入参
        JCTree.JCVariableDecl param = treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER), jcVariableDecl.getName(),jcVariableDecl.vartype, null);

        List<JCTree.JCVariableDecl> paramList = List.of(param);

        return treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC), // 方法限定值
                setNewMethodName(jcVariableDecl.getName()), // 方法名
                treeMaker.Type(new Type.JCVoidType()), // 返回类型
                List.nil(),
                paramList, // 入参
                List.nil(),
                body,
                null
        );

    }

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

4.创建测试Class

import com.hinotoyk.jsr269.Getter;
import com.hinotoyk.jsr269.Setter;

@Setter
@Getter
public class TestDemo {

    private String name;

    public static void main(String[] args) {
        TestDemo testDemo = new TestDemo();
        testDemo.setName("yk");
        System.out.println(testDemo.getName());
    }
}

5.运行

(1)第一步:创建.sh文件,做相应的编译设置 

(2)第二步:执行.sh文件

6.查看效果

参考:

        深入拆解 Java 虚拟机 - 郑雨迪 - 极客时间

        JVM系列之:你知道Lombok是如何工作的吗 - 掘金 (juejin.cn)

        从JSR269到Lombok,学习注解处理器Annotation Processor Tool - 掘金 (juejin.cn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值