栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Lombok-手写Lombok Setter注解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Lombok-手写Lombok Setter注解

Lombok Lombok注解
  • val 局部变量为final
  • @NonNull 对方法参数空检验,如果为空,抛出NullPointerException
  • @CleanUp 局部变量在执行完毕之后try-catch自动清除
  • @Getter @Setter
  • @ToString
  • @EqualsAndHashCode
  • @NoArgsConstructor @RequiredArgsConstructor @AllArgsConstructor
  • @Data
  • @Value 为类的属性变为final, 只提供getter方法
  • @Builder
  • @SneakyThrows 自动跑出异常
  • @Getter(lazy=true)
  • @Log (自动在类中注入日志对象)
    • @CommonsLog
    • @Log
    • @Log4j
    • @Log4j2
    • @Slf4j
    • XSlf4j
Lombok执行流程
  • java源码
  • 解析转化java文件
  • AST抽象语法树解析
  • Annotation Processing
    • Lombok注解处理
    • AST
    • Lombok注解Handler
  • AST语法树修改完成
  • 分析并生成class文件
Lombok使用
@Getter
class Lombok{
    public String name = "lombok";

    public static void main(String[] args) {
        Lombok lombok = new Lombok();
        System.out.println(lombok.getName());
    }
}
  • 生成class文件
class Lombok {
    public String name = "lombok";

    Lombok() {
    }

    public static void main(String[] args) {
        Lombok lombok = new Lombok();
        System.out.println(lombok.getName());
    }

    public String getName() {
        return this.name;
    }
}
手写Lombok Setter
package test;

import com.sun.source.tree.Tree;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeTranslator;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import com.sun.tools.javac.util.Context;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Set;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import javax.lang.model.element.Element;
import javax.tools.Diagnostic;
import com.sun.tools.javac.code.Flags;

@Retention(RetentionPolicy.SOURCE) // 保留位置:源码
@Target(ElementType.TYPE) // 位置: 类
@interface MySetter {

}

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("test.MySetter")
public class MySetterProcessor extends AbstractProcessor{

    private Messager messager; // 编译时期的输出日志
    private JavacTrees javacTrees; // 提供了待处理的抽象语法树
    private TreeMaker treeMaker; // 封装了创建AST节点的一写方法
    private Names names; // 提供了创建标识符的方法

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

    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnv) {
        // 获取所有的@MyGetter并循环修改
        Set elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(MySetter.class);
        elementsAnnotatedWith.forEach(e -> {
            // JCTree为AST元素的基类
            JCTree tree = javacTrees.getTree(e);
            // visit the tree with given visitor ()
            tree.accept(new TreeTranslator() { // subclass of visitor, left to right down to the tree
                // visit Class
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                    // empty list
                    List jcVariableDeclList = List.nil();
                    // 在抽象树中找出所有的变量
                    // jcClassDecl.defs 标识该class下所有的variables and methods
                    for (JCTree jcTree : jcClassDecl.defs) {
                        // 子节点为变量
                        if (jcTree.getKind().equals(Tree.Kind.VARIABLE)) {
                            // 强转为变量
                            JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) jcTree;
                            // 添加到jcVariableDeclList
                            jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
                        }
                    }
                    // 对于每一个变量进行生成方法的操作
                    jcVariableDeclList.forEach(jcVariableDecl -> {
                        messager.printMessage(Diagnostic.Kind.NOTE, jcVariableDecl.getName() + " has been processed");
                        //  given element to front of list, forming and returning a new list
                        jcClassDecl.defs = jcClassDecl.defs.prepend(makeSetterMethodDecl(jcVariableDecl));
                    });
                    super.visitClassDef(jcClassDecl);
                }
            });
        });
        return true;
    }

    // 创建Getter方法
    private JCTree.JCMethodDecl makeSetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
        // body ListBuffer
        ListBuffer statements = new ListBuffer<>();
        // 生成表达式 例如 this.a = a;
        // lhs 左边 this.a
        // rhs 右边 a
        // Select(this, a)
        JCTree.JCexpressionStatement aThis = makeAssignment(treeMaker.Select(treeMaker.Ident(
                names.fromString("this")), jcVariableDecl.getName()), treeMaker.Ident(jcVariableDecl.getName()));
        statements.append(aThis);
        // Block 代码块
        JCTree.JCBlock block = treeMaker.Block(0, statements.toList());

        // 生成入参(包含type)
        // 修改类型为方法参数 变量名 变量类型(String) JCexpression
        // String name
        JCTree.JCVariableDecl param = treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER),
                jcVariableDecl.getName(), jcVariableDecl.vartype, null);
        List parameters = List.of(param);

        // 生成返回类型 void
        JCTree.JCexpression methodType = treeMaker.Type(new Type.JCVoidType());
        // MethodDef 方法定义
        // PUBLIC 方法名 返回类型(void) 形参类型(当前入参中有type) 入参 抛出异常 body 默认值
        return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC),
                getNewMethodName(jcVariableDecl.getName()), methodType, List.nil(),
                parameters, List.nil(), block, null);
        // public void setName(String name) {
        //  this.name = name;
        // }
    }

    // 小驼峰命名法
    private Name getNewMethodName(Name name) {
        String s = name.toString();
        return names.fromString("set" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
    }

    private JCTree.JCexpressionStatement makeAssignment(JCTree.JCexpression lhs, JCTree.JCexpression rhs) {
        return treeMaker.Exec(
                treeMaker.Assign(
                        lhs,
                        rhs
                )
        );
    }
}
  • 命令编译文件
1. 编译自定义注解器和自定义注解处理器(必须使用tools.jar)
// 当前编译为jdk8编译(jdk8/lib/tools.jar)
javac -cp $JAVA_HOME/lib/tools.jar MySetter* -d .
// 也可以用idea编译为class文件
2. 使用自定义注解处理器来编译文件
javac -processor test.MySetterProcessor LomTest.java
3. 通过idea查看编译之后并反编译的class文件
  • 测试类
package test;

@MySetter
public class LombokTest {
    public String name;
}
  • 生成class文件
package test;

public class LombokTest {
    public String name;

    public void setName(String var1) {
        this.name = var1;
    }

    public LombokTest() {
    }
}
关于插件
  • lombok在idea编译需要lombok插件的支持,要不会报错
@MySetter
public class LombokTest {
    public String name;

    public static void main(String[] args) {
        LombokTest l = new LombokTest();   
        l.setName("lombok"); // 报错
        // 编译程序无法识别到setName方法所以报错
        // 加上插件之后
        // 推测: 加上插件之后,编译器前端将该文件放到lombok插件的server中检查
        // lombok插件的server识别该为正常类型,而且lombok server可以返回输入		// 提示
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/314212.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号