IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Lombok-手写Lombok Setter注解 -> 正文阅读

[Java知识库]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<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // 获取所有的@MyGetter并循环修改
        Set<? extends Element> 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<JCTree.JCVariableDecl> 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<JCTree.JCStatement> 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<JCTree.JCVariableDecl> 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可以返回输入		// 提示
    }
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-12 23:18:12  更:2021-10-12 23:19:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 21:00:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码