• 【lombok原理】无聊的周末一个人手写一个lombok



    作者: 西魏陶渊明
    博客: https://blog.springlearn.cn/

    天下代码一大抄, 抄来抄去有提高, 看你会抄不会抄!

    一、什么是APT

    APT 全拼 Annotation Processing Tool 处理器,是 javac 的一个工具,他可以在源码生成class的时候,处理Java语法树。我们用他可以干什么呢? 当然是折腾了, 提高眼界。

    如果你用过lombok的话, 就会发现lombok的原理,就是在编译期修改字节码,生成 getset 方法。

    本文我们就定义一个处理器,然后来自动生成get和set方法。

    源码地址

    感兴趣的可以了解一下,不用害怕,尤其是2.6到2.7目录,不需要看到这个API就害怕。也不用死记硬背。了解就好。
    正经人是不会用这个生成字节码的,字节码生成我们有很多好用的工具。

    或者是 javapoet

    二、实战演示

    2.1 定义处理器

    继承 AbstractProcessor

    @AutoService(Processor.class)
    @SupportedAnnotationTypes({"cn.lxchinesszz.MyData","cn.lxchinesszz.MyGetter","cn.lxchinesszz.MySetter"})
    // 这个注解处理器是处理哪个注解的
    @SupportedSourceVersion(SourceVersion.RELEASE_8)
    public class MyLombokProcessor extends AbstractProcessor {
    
        @Override
        public synchronized void init(ProcessingEnvironment processingEnvironment) {}
    
        @Override
        public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • @AutoService 谷歌提供的SPI工具。当使用这个注解会自定生成Java SPI文件, 当然如果不想用谷歌的工具,我们也可以自己来写配置文件
    ├── classes
    │   ├── META-INF
    │   │   └── services
    │   │       └── javax.annotation.processing.Processor
    
    • 1
    • 2
    • 3
    • 4
    • @SupportedAnnotationTypes({“cn.lxchinesszz.MyData”,“cn.lxchinesszz.MyGetter”,“cn.lxchinesszz.MySetter”})

    支持的注解类型

    • @SupportedSourceVersion(SourceVersion.RELEASE_8)

    支持的源码类型

    2.2 Element 体系

    • roundEnv.getElementsAnnotatedWith(MyData.class) 可以获取被该注解修饰的类或者字段或者方法。

    下面我们看下 Element 的类型。

    public class User{ // TypeElement
    
        private String name; // VariableElement
        
        private Interge age; // VariableElement
        
        public String getName(){ // ExecutableElement
            return this.name;
        }
        
        public void setName( // ExecutableElement
        String name // VariableElement
        ){
            this.name = name;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    如何知道Element 的类型呢。

    • Element#getKind

    2.2.1 获取字段信息

    这里我们先自定义一个字段类型来获取基础信息,来学习 Element

     public class FieldElement extends ModifierElement {
    
        /**
         * 字段名
         */
        private final String fieldName;
    
        /**
         * 字段类型
         */
        private Class<?> fieldType;
    
        /**
         * 资源原始类型
         */
        private final VariableElement fieldElement;
    
        /**
         * 基本类型提示
         */
        private String remark;
    
        /**
         * 字段所属类
         */
        private final TypeElement classElement;
    
        public FieldElement(String fieldName, VariableElement fieldElement) {
            super(fieldElement);
            this.fieldName = fieldName;
            this.fieldElement = fieldElement;
            this.classElement = (TypeElement) fieldElement.getEnclosingElement();
            try {
                if (isPrimitive()) {
                    fieldType = null;
                    this.remark = "基本类型:" + fieldElement.asType().toString();
                } else {
                    this.fieldType = Class.forName(fieldElement.asType().toString());
                }
            } catch (ClassNotFoundException e) {
                // 如果还报错说明是一个泛型 根据泛型类型来进行处理 fieldElement.asType()
                // DeclaredType    Set
                // WildcardType
                //    ?
                //    ? extends Number
                //    ? super T
                this.fieldType = Object.class;
            }
        }  
     }   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 首先先判断是字段类型
       public static FieldElement toFiledElement(Element enclosedElement) {
            if (ElementKind.FIELD.equals(enclosedElement.getKind())) {
                VariableElement fieldElement = (VariableElement) enclosedElement;
                Name simpleName = fieldElement.getSimpleName();
                return new FieldElement(simpleName.toString(), fieldElement);
            } else {
                throw new RuntimeException("enclosedElement 不是字段类型:" + enclosedElement);
            }
        }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.2.1 获取方法信息

    • 方法包括方法参数和返回值,这里我们自定义一个方法参数。
    public class MethodElement extends ModifierElement{
    
        /**
         * 方法参数名
         */
        private final String methodName;
    
        /**
         * 返回值
         */
        private Class<?> returnType;
    
        /**
         * 方法原始信息
         */
        private final ExecutableElement methodElement;
    
        /**
         * 方法参数
         */
        private final List<MethodParamElement> methodParamElements;
    
        public MethodElement(ExecutableElement methodElement, List<MethodParamElement> methodParamElements) {
            super(methodElement);
            this.methodName = methodElement.getSimpleName().toString();
            try {
                TypeMirror returnTypeMirror = methodElement.getReturnType();
                if (returnTypeMirror instanceof NoType) {
                    this.returnType = Void.TYPE;
                } else {
                    this.returnType = Class.forName(methodElement.getReturnType().toString());
                }
            } catch (ClassNotFoundException e) {
                this.returnType = Void.TYPE;
            }
            this.methodElement = methodElement;
            this.methodParamElements = methodParamElements;
        }
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 生成方法
        public static MethodParamElement toMethodParamElement(Element enclosedElement) {
            if (ElementKind.PARAMETER.equals(enclosedElement.getKind())) {
                VariableElement fieldElement = (VariableElement) enclosedElement;
                Name simpleName = fieldElement.getSimpleName();
                return new MethodParamElement(simpleName.toString(), fieldElement);
            } else {
                throw new RuntimeException("enclosedElement 不是字段类型:" + enclosedElement);
            }
        }
    
        public static MethodElement toMethodElement(Element enclosedElement) {
            if (ElementKind.METHOD.equals(enclosedElement.getKind())) {
                ExecutableElement methodElement = (ExecutableElement) enclosedElement;
                List<? extends VariableElement> parameters = methodElement.getParameters();
                List<MethodParamElement> paramElements = new ArrayList<>();
                for (VariableElement parameter : parameters) {
                    paramElements.add(toMethodParamElement(parameter));
                }
                return new MethodElement(methodElement, paramElements);
            } else {
                throw new RuntimeException("enclosedElement 不是方法类型:" + enclosedElement.getClass());
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2.2.2 获取类信息

    类信息包括字段和方法

    public class ClassElement extends ModifierElement {
    
        /**
         * 类名称
         */
        private final String className;
    
        /**
         * 包名称
         */
        private final String packageName;
    
        /**
         * 类原始信息
         */
        private final TypeElement classElement;
    
        /**
         * 字段信息
         */
        private final List<FieldElement> fieldElements;
    
        /**
         * 方法信息
         */
        private final List<MethodElement> methodElements;
    
        public ClassElement(Element enclosedElement, List<FieldElement> fieldElements, List<MethodElement> methodElements) {
            super(enclosedElement);
            this.classElement = (TypeElement) enclosedElement;
            this.fieldElements = fieldElements;
            this.methodElements = methodElements;
            this.className = classElement.getSimpleName().toString();
            this.packageName = classElement.getQualifiedName().toString().replaceAll("\\." + classElement.getSimpleName().toString(), "");
        }
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    生成类信息

    public static ClassElement toClassElement(Element enclosedElement) {
        if (ElementKind.CLASS.equals(enclosedElement.getKind())) {
            List<? extends Element> enclosedElements = enclosedElement.getEnclosedElements();
            List<FieldElement> fieldElements = new ArrayList<>();
            List<MethodElement> methodElements = new ArrayList<>();
            for (Element element : enclosedElements) {
                if (ElementKind.FIELD.equals(element.getKind())) {
                    fieldElements.add(toFiledElement(element));
                }
                if (ElementKind.METHOD.equals(element.getKind())) {
                    methodElements.add(toMethodElement(element));
                }
            }
            return new ClassElement(enclosedElement, fieldElements, methodElements);
        } else {
            throw new RuntimeException("enclosedElement 不是字段类型:" + enclosedElement);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2.3 日志打印

    APT方法中日志的打印,要使用工具。在初始化方法中获取消息打印实例。

    public class MyLombokProcessor extends AbstractProcessor {
        private Messager message;
    
         @Override
        public synchronized void init(ProcessingEnvironment processingEnvironment) {
            super.init(processingEnvironment);
            message = processingEnvironment.getMessager();
        }
    
        @Override
        public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {
            //	扫描所有被@MyData注解的元素
            processingEnv.getMessager().printMessage(NOTE, "------------MyData-----------" + roundEnv.getElementsAnnotatedWith(MyData.class));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    就想log日志一样,他也是有消息类型的,如: 提示、异常、警告等。如下枚举

    /**
     * 诊断类型,例如错误或警告。诊断的类型可用于确定应如何将诊断呈现给用户。例如,错误可能被涂成红色或以“错误”一词为前缀,
     * 而警告可能被涂成黄色或以“警告”一词为前缀。没有要求 Kind 应该对诊断消息暗示任何固有的语义含义:例如,一个工具可能会
     * 提供一个选项来将所有警告报告为错误。
     */
    enum Kind {
       /**
        * 阻止工具正常完成编译
        */
       ERROR,
       /**
        * 警告
        */
       WARNING,
       /**
        * 类似于警告的问题,但由工具规范强制要求。例如,Java™ 语言规范要求对某些未经检查的操作和使用过时的方法发出警告。
        */
       MANDATORY_WARNING,
       /**
        * 来自该工具的信息性消息。
        */
       NOTE,
       /**
        * 其他类型的诊断
        */
       OTHER,
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2.4 字节码修改

    字节码修改首先我们要拿到字节码语法树对象,通过观察者模式类进行修改。这里也在初始化时候获取工具。
    如下我们先定义一个工具。

    public class ClassElementBuilder {
        
        private ProcessingEnvironment processingEnv;
    
        private JavacTrees trees;
    
        protected Names names;
    
        protected TreeMaker treeMaker;
    
    
        public ClassElementBuilder(ProcessingEnvironment processingEnv) {
            Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
            this.processingEnv = processingEnv;
            this.trees = JavacTrees.instance(processingEnv);
            this.treeMaker = TreeMaker.instance(context);
            this.names = Names.instance(context);
        }
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    处理器初始化方法进行工具的实例化。

     @Override
     public synchronized void init(ProcessingEnvironment processingEnvironment) {
         super.init(processingEnvironment);
         this.classElementBuilder = new ClassElementBuilder(processingEnvironment);
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    此时我们就能对添加和修改语法树了。但是这里我们先不着急, 我们在先学习一下语法树的API。

    2.5 JCTree 语法树

    2.5.1 定义字段

    定义变量使用

    • TreeMaker#VarDef(JCTree.JCModifiers 字段修饰符,Names 字段名,JCExpression 字段类型,JCExpression 赋值语句)
    private String ${fieldName};
    private JCTree.JCVariableDecl generateStringField(JCTree.JCClassDecl jcClassDecl, String fieldName) {
        JCTree.JCVariableDecl var = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PRIVATE),
                names.fromString(fieldName),
                treeMaker.Ident(names.fromString("String")),
                null);
        jcClassDecl.defs = jcClassDecl.defs.prepend(var);
        return var;
    }
    
    private String ${fieldName} = ${fieldName}
    private JCTree.JCVariableDecl generateStringField(JCTree.JCClassDecl jcClassDecl, String fieldName) {
        // 字段的赋值语句
        JCTree.JCVariableDecl var = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PRIVATE),
                names.fromString(fieldName),
                treeMaker.Ident(names.fromString("String")),
                treeMaker.Literal(fieldName));
    
        jcClassDecl.defs = jcClassDecl.defs.prepend(var);
        return var;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    要想理解这个API,实现要分析字段是由什么构成的,正如下图。

    标示符三种处理方式。

    1. 包装类型,不用引入包,可以直接使用
    • TreeMaker#Ident
      JCExpression
    treeMaker.Ident(names.fromString("String"))
    
    • 1
    1. 基本类型,不用引入包,可以直接使用
    • TreeMaker#TypeIdent
    treeMaker.TypeIdent(TypeTag.INT)
    
    • 1
    1. 引用类型,需要引入包后再直接使用
    • 先引入包,然后就向包装类型那样进行处理。
    // import package
    private JCTree.JCImport genImportPkg(String packageName, String className) {
        JCTree.JCIdent ident = treeMaker.Ident(names.fromString(packageName));
        return treeMaker.Import(treeMaker.Select(
                ident, names.fromString(className)), false);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.5.2 定义get和set方法

    生成set方法,方法是由

    • 方法修饰符 treeMaker.Modifiers(Flags.PUBLIC + Flags.STATIC + Flags.FINAL)
    • 方法名 names.fromString("setName")
    • 方法返回值 treeMaker#Type、treeMaker#TypeIdent
    /**
     * public void setName(String name){
     *      this.name = name;
     * }
     *
     * @param jcClassDecl 类
     * @param f           字段
     * @param fieldName   字段名
     */
    private void generateSetMethod(JCTree.JCClassDecl jcClassDecl, JCTree.JCVariableDecl f, String fieldName) {
        // 方法体内容
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        // this.MyDate
        JCTree.JCFieldAccess aThis = treeMaker.Select(treeMaker.Ident(names.fromString("this")), names.fromString(fieldName));
        // this.MyDate = MyDate;
        JCTree.JCExpressionStatement exec = treeMaker.Exec(treeMaker.Assign(aThis, treeMaker.Ident(names.fromString(fieldName))));
        statements.add(exec);
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
    
        // 方法参数
        JCTree.JCVariableDecl param = treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER), names.fromString(fieldName), f.vartype, null);
        com.sun.tools.javac.util.List<JCTree.JCVariableDecl> parameters = com.sun.tools.javac.util.List.of(param);
    
        JCTree.JCMethodDecl getNameMethod = treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC),  // 方法修饰符
                names.fromString("set" + capRename(fieldName)),  // 方法名,capName转驼峰
                treeMaker.Type(new Type.JCVoidType()),  // 方法返回值类型
                List.nil(),
                parameters, // 方法参数
                List.nil(),
                body,// 方法体
                null
        );
        // 插入到语法树中
        jcClassDecl.defs = jcClassDecl.defs.prepend(getNameMethod);
    }
    
    /**
     * public void getName(){
     *    return this.name;
     * }
     *
     * @param jcClassDecl 类
     * @param f           字段
     * @param fieldName   字段名
     */
    private void generateGetMethod(JCTree.JCClassDecl jcClassDecl, JCTree.JCVariableDecl f, String fieldName) {
        // 方法体内容
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        // this.name
        JCTree.JCFieldAccess select = treeMaker.Select(treeMaker.Ident(names.fromString("this")),
                names.fromString(fieldName));
        // 生成return代码 return this.name
        JCTree.JCReturn jcReturn = treeMaker.Return(select);
        statements.add(jcReturn);
        // 方法体
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
        // 生成方法
        JCTree.JCMethodDecl getNameMethod = treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC), // 方法修饰符
                names.fromString("get" + capRename(fieldName)), // 方法名
                f.vartype, // 方法返回值类型
                List.nil(),
                List.nil(), // 方法参数
                List.nil(),
                body, // 方法体
                null
        );
        // 插入到语法树中
        jcClassDecl.defs = jcClassDecl.defs.prepend(getNameMethod);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    参考文章

    2.6 TreeMaker

    部分内容翻译自

    2.6.1 Modifiers 修饰符

    java中类,方法,字段都是有修饰符的。

    public JCModifiers Modifiers(long flags) {
        return Modifiers(flags, List.< JCAnnotation >nil());
    }
    
    public JCModifiers Modifiers(long flags,
        List<JCAnnotation> annotations) {
            JCModifiers tree = new JCModifiers(flags, annotations);
            boolean noFlags = (flags & (Flags.ModifierFlags | Flags.ANNOTATION)) == 0;
            tree.pos = (noFlags && annotations.isEmpty()) ? Position.NOPOS : pos;
            return tree;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 定义多个修饰符

    treeMaker.Modifiers(Flags.PUBLIC + Flags.STATIC + Flags.FINAL);

    2.6.2 ClassDef 定义类

    
    public JCClassDecl ClassDef(JCModifiers mods, // 访问标志,可以通过 TreeMaker.Modifiers 来创建
        Name name, // 类名
        List<JCTypeParameter> typarams, // 泛型参数列表
        JCExpression extending, // 父类
        List<JCExpression> implementing, // 实现的接口
        List<JCTree> defs) { // 类定义的详细语句,包括字段、方法的定义等等
            JCClassDecl tree = new JCClassDecl(mods,
                                         name,
                                         typarams,
                                         extending,
                                         implementing,
                                         defs,
                                         null);
            tree.pos = pos;
            return tree;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2.6.3 MethodDef 定义方法

    public JCMethodDecl MethodDef(JCModifiers mods, // mods:访问标志
        Name name, // 方法名
        JCExpression restype, // 返回类型,返回类型 restype 填写 null 或者 treeMaker.TypeIdent(TypeTag.VOID) 都代表返回 void 类型
        List<JCTypeParameter> typarams, // 泛型参数列表
        List<JCVariableDecl> params, // 参数列表
        List<JCExpression> thrown, // 异常声明列表
        JCBlock body, // 方法体
        JCExpression defaultValue // 默认方法(可能是 interface 中的哪个 default)
        ) {
            JCMethodDecl tree = new JCMethodDecl(mods,
                                           name,
                                           restype,
                                           typarams,
                                           params,
                                           thrown,
                                           body,
                                           defaultValue,
                                           null);
            tree.pos = pos;
            return tree;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2.6.4 VarDef 定义字段

    public JCVariableDecl VarDef(JCModifiers mods, // 访问标志
        Name name, // 参数名称
        JCExpression vartype, // 类型
        JCExpression init // 初始化语句
        ) {
            JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, null);
            tree.pos = pos;
            return tree;
    }
    
    public JCVariableDecl VarDef(VarSymbol v,
        JCExpression init) {
            return (JCVariableDecl)
                new JCVariableDecl(
                    Modifiers(v.flags(), Annotations(v.getAnnotationMirrors())),
                    v.name,
                    Type(v.type),
                    init,
                    v).setPos(pos).setType(v.type);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.6.5 Return 定义返回

    public JCReturn Return(JCExpression expr) {
            JCReturn tree = new JCReturn(expr);
            tree.pos = pos;
            return tree;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.6.6 Ident 定义关键字

    • treeMaker.Ident(names.fromString("this"))
    public JCIdent Ident(Name name) {
            JCIdent tree = new JCIdent(name, null);
            tree.pos = pos;
            return tree;
    }
    
    public JCIdent Ident(Symbol sym) {
            return (JCIdent)new JCIdent((sym.name != names.empty)
                                    ? sym.name
                                    : sym.flatName(), sym)
                .setPos(pos)
                .setType(sym.type);
    }
    
    public JCExpression Ident(JCVariableDecl param) {
            return Ident(param.sym);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2.6.7 Select

    TreeMaker.Select 用于创建域访问 / 方法访问

    public JCFieldAccess Select(JCExpression selected,
        Name selector) 
    {
            JCFieldAccess tree = new JCFieldAccess(selected, selector, null);
            tree.pos = pos;
            return tree;
    }
    
    public JCExpression Select(JCExpression base,
        Symbol sym) {
            return new JCFieldAccess(base, sym.name, sym).setPos(pos).setType(sym.type);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    两个参数第一个是对象,第二个是对象的那个方法。

    • this.${fieldName}
            JCTree.JCFieldAccess aThis = treeMaker.Select(treeMaker.Ident(names.fromString("this")), names.fromString(fieldName));
    
    • 1

    2.6.8 NewClass

    用于创建 new 语句语法树节点(JCNewClass)

    public JCNewClass NewClass(JCExpression encl,
        List<JCExpression> typeargs, // 参数类型列表
        JCExpression clazz, // 待创建对象的类型
        List<JCExpression> args, // 参数列表
        JCClassDecl def) { // 类定义
            JCNewClass tree = new JCNewClass(encl, typeargs, clazz, args, def);
            tree.pos = pos;
            return tree;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.6.9 Assign 赋值语句

    类似 Select, 如果说Select是 .
    那么Assign是 =

    public JCAssign Assign(JCExpression lhs,
        JCExpression rhs) {
        JCAssign tree = new JCAssign(lhs, rhs);
        tree.pos = pos;
        return tree;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    select和assign配合使用

            // this.MyDate
            JCTree.JCFieldAccess aThis = treeMaker.Select(treeMaker.Ident(names.fromString("this")), names.fromString(fieldName));
            // this.MyDate = MyDate;
            JCTree.JCExpressionStatement exec = treeMaker.Exec(treeMaker.Assign(aThis, treeMaker.Ident(names.fromString(fieldName))));
          
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.6.10 Exec执行语句

    配合赋值语句使用

    public JCExpressionStatement Exec(JCExpression expr) {
            JCExpressionStatement tree = new JCExpressionStatement(expr);
            tree.pos = pos;
            return tree;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.6.11 Apply 方法调用

    // 创建一个方法调用 user.say("hello world!");
    JCTree.JCExpressionStatement exec2 = treeMaker.Exec(
            treeMaker.Apply(
                    com.sun.tools.javac.util.List.nil(),
                    treeMaker.Select(
                            treeMaker.Ident(names.fromString("user")), // . 左边的内容
                            names.fromString("say") // . 右边的内容
                    ),
                    com.sun.tools.javac.util.List.of(treeMaker.Literal("hello world!")) // 方法中的内容
            )
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.7 代码片段

    2.6的API,是真的难用,这里列几个片段。

    2.7.1 生成字段

    • private String age;
    // 生成参数 例如:private String age;
    treeMaker.VarDef(treeMaker.Modifiers(Flags.PRIVATE), names.fromString("age"), treeMaker.Ident(names.fromString("String")), null);
    
    • 1
    • 2

    2.7.2 赋值变量

    • private String name = "西魏陶渊明"
    treeMaker.VarDef(treeMaker.Modifiers(Flags.PRIVATE),names.fromString("name"),treeMaker.Ident(names.fromString("String")),treeMaker.Literal("西魏陶渊明"))
    
    
    • 1
    • 2

    2.7.3 相加

    • JCTree.Tag.PLUS
    treeMaker.Exec(treeMaker.Assign(treeMaker.Ident(names.fromString("add")),treeMaker.Binary(JCTree.Tag.PLUS,treeMaker.Literal("a"),treeMaker.Literal("b"))))
    
    • 1

    2.7.4 += 语法

    • JCTree.Tag.PLUS_ASG
    treeMaker.Exec(treeMaker.Assignop(JCTree.Tag.PLUS_ASG, treeMaker.Ident(names.fromString("add")),
    treeMaker.Literal("test")))
    
    • 1
    • 2

    2.7.5 ++ 语法

    treeMaker.Exec(treeMaker.Unary(JCTree.Tag.PREINC,treeMaker.Ident(names.fromString("i"))))
    
    • 1

    2.7.6 If语句

    /*
        创建一个if语句
        if("BuXueWuShu".equals(name)){
            add = "a" + "b";
        }else{
            add += "test";
        }
     */
    // "BuXueWuShu".equals(name)
    JCTree.JCMethodInvocation apply = treeMaker.Apply(
            com.sun.tools.javac.util.List.nil(),
            treeMaker.Select(
                    treeMaker.Literal("BuXueWuShu"), // . 左边的内容
                    names.fromString("equals") // . 右边的内容
            ),
            com.sun.tools.javac.util.List.of(treeMaker.Ident(names.fromString("name")))
    );
    //  add = "a" + "b"
    JCTree.JCExpressionStatement exec3 = treeMaker.Exec(treeMaker.Assign(treeMaker.Ident(names.fromString("add")), treeMaker.Binary(JCTree.Tag.PLUS, treeMaker.Literal("a"), treeMaker.Literal("b"))));
    //  add += "test"
    JCTree.JCExpressionStatement exec1 = treeMaker.Exec(treeMaker.Assignop(JCTree.Tag.PLUS_ASG, treeMaker.Ident(names.fromString("add")), treeMaker.Literal("test")));
    
    JCTree.JCIf anIf = treeMaker.If(
            apply, // if语句里面的判断语句
            exec3, // 条件成立的语句
            exec1  // 条件不成立的语句
    );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    推荐环节

    最后小编还写了免费的关于Mybatis的源码学习系列和手撸JavaRPC系列,从网络通信开始,从0到1实现一个RPC框架爱,如果感兴趣的话,点个关注吧,交个朋友吧。

    天下代码一大抄,抄来抄去有提高,看你会抄不会抄,跟我一起来写代码吧。

    😊 那么你准备好跟我一起Coding了吗?,如果喜欢麻烦点个关注。

  • 相关阅读:
    设计模式—创建型模式之单例模式
    Redis持久化策略
    CodeQL使用流程
    MMdetection训练配置
    HJ58 输入n个整数,输出其中最小的k个
    【javaweb】学习日记Day8 - Mybatis入门 Mysql 多表查询 事务 索引
    iotdb优化
    算法设计与分析 | 分治棋盘
    ant design vue treeDefaultExpandAll 更换数据后没有自动展开
    【每日一题】CF1680C. Binary String | 双指针 | 简单
  • 原文地址:https://blog.csdn.net/Message_lx/article/details/126695402