消除代码冗长神器 - Lombok | 高阶用法 -> 手写 @Getter 注解

发布时间:2024年01月11日


😛 是不是很好奇 Lombok 的实现原理?
?? 答案:使用 Java 提供的 Annotation Processor 实现,在编译时修改源代码。
👉 本文目标:

  • 了解 Java 中的 Annotation Processor
  • 像 Lombok 一样,手写一个 @Getter 注解实现添加 getter 方法
  • 了解 Lombok 注解处理器,并调试之

必备知识 1 - Java 注解处理器

注解历史及 javac 相关选项

在 Java 5 中初次引入了注解,此时注解处理需要额外的工具,即 apt(Annotation Processing Tool) 来处理。
在 Java 6 中将注解处理内置到了 javac 中,就不需要额外的 apt 来处理了。相关新增选项如下:

  • -proc:{none, only}:控制是否执行注解处理或编译
  • -processor:指定要运行的注解处理器的名称,会绕过默认的搜索进程
  • -processorpath:指定搜索注解处理器的路径
  • -XprintRounds:输出注解注解处理轮次信息
  • -XprintProcessorInfo:输出请求注解处理器处理哪些注解的信息

image.png
image.png

注解处理相关类

Java SE6 中注解处理的主要包:

  • javax.annotation.processing:注解处理器核心包,主要包括 Processor 接口、AbstractProcessor 抽象类、Messager 接口等
  • javax.lang.model:比如 SourceVersion 枚举,作为当前 Java 的源码版本
  • javax.tools:有一些有用的类,比如 Diagnostic$Kind 枚举,用于表示诊断的问题类型【类似于日志级别】

Processor 接口 - 注解处理器接口

注解是按照轮次(round)来处理的。

  • 每一轮中,都会使用上一轮的结果进行过滤匹配。
  • 至于运行哪一个注解处理器,则取决于 root 元素上的注解类型、处理器支持的注解类型。

调用逻辑

  • 调用构造方法:工具调用 Processor 实现类的构造方法(必须是 public 类型的无参构造器)
  • 调用 init() 方法:调用 Processor 实现类中的 init(ProcessingEnvironment) 方法
  • 判断是否支持处理特定注解:调用 getSupportedAnnotationTypes()、getSupportedOptions()、getSupportedSourceVersion()方法,确定下当前注解处理器是否适用
  • 执行 process() 方法:执行处理逻辑,每一轮中都会复用当前 Processor 对象。

发现机制:有如下三种方式

  • Java 编译器可以直接设置候选的注解处理器:javax.tools.JavaCompiler.CompilationTask#setProcessors
  • 使用一个路径来搜索注解处理器:javax.tools.StandardLocation ANNOTATION_PROCESSOR_PATH()
  • 使用命令行选项来指定 javac 选项

接口方法

  • Set getSupportedOptions():获取支持的选项
  • Set getSupportedAnnotationTypes():获取支持的注解类型
  • SourceVersion getSupportedSourceVersion():当前注解处理器支持的最大 Java 源码版本
  • void init(ProcessingEnvironment processingEnv):初始化方法,ProcessingEnvironment 是处理器级别的环境信息
  • boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv):处理方法,annotations 为对应的注解类型,RoundEnvironment 为轮次环境信息。如果返回 true,则表示后续处理器不会进行处理;如果返回 false,则后续处理器会进行处理。
  • Iterable<? extends Completion> getCompletions(Element element,AnnotationMirror annotation,ExecutableElement member,String userText):获取轮次

AbstractProcessor - 自定义注解处理器的基类

上述 Processor 接口,不推荐自行实现。推荐继承 AbstractProcessor 抽象类来实现自定义的注解处理器。
AbstractProcessor 抽象类:作为 Processor 接口的实现类,作为自定义注解处理器的基类。其主要做了如下实现:

  • getSupportedOptions:使用注解 @SupportedOptions 来配置支持的选项
  • getSupportedAnnotationTypes:使用注解 @SupportedAnnotationTypes 来配置支持的注解类型,包类名方式。
    • 示例 1:name.*
    • 示例 2:*,表示所有注解类型。不推荐使用,可能存在性能降低问题
  • getSupportedSourceVersion:使用注解 @SupportedSourceVersion 配置支持的最大源码版本,默认是 JDK 1.6
  • init:初始化方法,持有 ProcessingEnvironment 对象并标记为已初始化
  • getCompletions:获取轮次,返回空的集合

ProcessingEnvironment - 处理环境信息

主要功能:

  • 获取处理器选项:Map<String,String> getOptions();
  • 获取 Messager 对象,用于输出日志:Messager getMessager();
  • 获取文件对象,用于创建源码、字节码或辅助文件:Filer getFiler();
  • 获取操作 Element 的工具类:Elements getElementUtils();
  • 获取操作 Type 的工具类:Types getTypeUtils();
  • 获取支持的源码版本:SourceVersion getSourceVersion();
  • 获取本地语言:Locale getLocale();

RoundEnvironment - 每一轮的环境信息

主要功能:

  • 是否处理完毕,返回 true 表示处理完毕,不用下一轮处理:boolean processingOver();
  • 上一轮处理是否有异常抛出:boolean errorRaised();
  • 返回第一轮处理产生的元素:Set<? extends Element> getRootElements();
  • 根据指定 TypeElement 获取元素:Set<? extends Element> getElementsAnnotatedWith(TypeElement a);
  • 根据指定注解获取元素:Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a);

必备知识 2 - AST 抽象语法树

上面讲述的是注解处理器,下面讲述如何通过 AST 抽象语法树修改源代码。

AST 简介

AST,Abstract Syntax Tree,抽象语法树:用于表示源代码的抽象语法结构,一般是编译器会使用到它。
Java 源代码示例:

// Main class 
public class GFG { 
	// Main driver method 
	public static void main(String[] args) 
	{ 
		// Print statement 
		System.out.println("Hello World!"); 
	} 
}

对应的抽象语法树如下:

CLASS_DEF -> CLASS_DEF [1:0] 
|--MODIFIERS -> MODIFIERS [1:0] 
| `--LITERAL_PUBLIC -> public [1:0] 
|--LITERAL_CLASS -> class [1:7] 
|--IDENT -> GFG [1:13] 
`--OBJBLOCK -> OBJBLOCK [1:17] 
	|--LCURLY -> { [1:17] 
	|--METHOD_DEF -> METHOD_DEF [2:4] 
	| |--MODIFIERS -> MODIFIERS [2:4] 
	| | |--LITERAL_PUBLIC -> public [2:4] 
	| | `--LITERAL_STATIC -> static [2:11] 
	| |--TYPE -> TYPE [2:18] 
	| | `--LITERAL_VOID -> void [2:18] 
	| |--IDENT -> main [2:23] 
	| |--LPAREN -> ( [2:27] 
	| |--PARAMETERS -> PARAMETERS [2:34] 
	| | `--PARAMETER_DEF -> PARAMETER_DEF [2:34] 
	| |	 |--MODIFIERS -> MODIFIERS [2:34] 
	| |	 |--TYPE -> TYPE [2:34] 
	| |	 | `--ARRAY_DECLARATOR -> [ [2:34] 
	| |	 |	 |--IDENT -> String [2:28] 
	| |	 |	 `--RBRACK -> ] [2:35] 
	| |	 `--IDENT -> args [2:37] 
	| |--RPAREN -> ) [2:41] 
	| `--SLIST -> { [2:43] 
	|	 |--EXPR -> EXPR [3:26] 
	|	 | `--METHOD_CALL -> ( [3:26] 
	|	 |	 |--DOT -> . [3:18] 
	|	 |	 | |--DOT -> . [3:14] 
	|	 |	 | | |--IDENT -> System [3:8] 
	|	 |	 | | `--IDENT -> out [3:15] 
	|	 |	 | `--IDENT -> println [3:19] 
	|	 |	 |--ELIST -> ELIST [3:27] 
	|	 |	 | `--EXPR -> EXPR [3:27] 
	|	 |	 |	 `--STRING_LITERAL -> "Hello World!" [3:27] 
	|	 |	 `--RPAREN -> ) [3:41] 
	|	 |--SEMI -> ; [3:42] 
	|	 `--RCURLY -> } [4:4] 
	`--RCURLY -> } [5:0] 

java 中的 AST

Java 中的 AST,使用 JCTree 类来替代。
该类中包含大量静态内部类:

  • JCClassDecl:类定义
  • JCModifiers:访问修饰符
  • JCMethodDecl:方法定义
  • JCIdent:变量、类型、关键字
  • JCVariableDecl:字段
  • JCAssign:赋值
  • JCReturn:return

还有一些辅助的类:

  • Names:命名工具类
  • TreeMaker:创建树结构的工具类

下图可以比较好的解释树中节点对应的 Java 类:
48a1f4e3b21a416eb2110eeac21a914d.png

如何给类添加新方法?

假设有如下源码:

public class User {
    private String name;
    private int age;
}

则对应的 JCTree 对象如下:在 defs 属性上可以看到 1 个方法定义、两个变量定义
image.png
**在上述 JCTree 的 defs 列表中加入新的元素,就可以实现新增方法啦。**😎

实战 - 手写 Lombok @Getter 注解

了解上述原理后,我们手写一个 @Getter 注解,实现属性的 getter 方法的生成。lombok-demo.zip
创建两个模块:

  • processor:提供 @Getter 注解及注解处理器
  • demo:使用 @Getter 注解自动生成 getter 方法

image.png

processor 模块 - 提供方

processor 模块主要干两件事:

  • 提供 @Getter 注解
  • 提供 @Getter 注解的注解处理器

@Getter 注解

@Getter 注解定义非常简单,直接定义注解存放位置(类、字段)及在源码上保留。

/**
 * Getter 注解,用于自动生成 public 类型的 Getter 方法
 *
 * @author demo
 */
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {

}

SetterGetterAnnotationProcessor 注解处理器

注解处理器代码如下:

package com.lombok.processor;

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.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Name;
import com.sun.tools.javac.util.Names;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
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 注解处理器
 *
 * @author demo
 */
@SupportedAnnotationTypes("com.lombok.annotation.Getter")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class GetterAnnotationProcessor extends AbstractProcessor {

    // 用于输出日志
    private Messager messager;
    // java 处理环境的上下文
    private Context context;
    // 命名工具类
    private Names names;

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

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        messager.printMessage(Diagnostic.Kind.NOTE, "进入 GetterAnnotationProcessor.process() 方法");

        // 正常来说,annotations 里只有一个注解,就是 com.lombok.annotation.Getter
        for (TypeElement typeElement : annotations) {
            // 获取所有具备此注解的元素
            Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(typeElement);
            set.forEach(element -> {
                // 遍历元素,并找到其抽象语法树
                JCTree jcTree = JavacTrees.instance(processingEnv).getTree(element);
                // 访问类定义的时候,查找类中的变量定义,并给这些变量添加 getter 方法
                jcTree.accept(new TreeTranslator() {
                    @Override
                    public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                        // 遍历所有定义
                        for (JCTree tree : jcClassDecl.defs) {
                            if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
                                // 对所有变量添加 getter 方法定义
                                JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
                                messager.printMessage(Diagnostic.Kind.NOTE, "正在为 " + jcVariableDecl.getName() + " 添加 getter 方法");
                                jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
                            }
                        }

                        super.visitClassDef(jcClassDecl);
                    }
                });
            });
        }

        // 返回 true,表示后续处理器不会继续处理了
        return true;
    }

    private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
        TreeMaker treeMaker = TreeMaker.instance(context);
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        // return this.name;
        statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName())));
        // {\r\n return this.name; \r\n}
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
        // public String getName() {
        //    return this.name;
        //}
        return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), getGetMethodName(jcVariableDecl.getName()), jcVariableDecl.vartype,
                List.nil(), List.nil(), List.nil(), body, null);
    }

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

有些难懂,拆解一下。😂

创建 SetterGetterAnnotationProcessor 类

首先,先创建一个 SetterGetterAnnotationProcessor 类,其需要继承注解处理器的基类。
其次,指定注解处理器仅处理 SetterGetter 注解,即 @SupportedAnnotationTypes(“com.lombok.annotation.SetterGetter”)
最后,指定注解处理器支持的最大源码版本,即 @SupportedSourceVersion(SourceVersion.RELEASE_8)

@SupportedAnnotationTypes("com.lombok.annotation.Getter")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class GetterAnnotationProcessor extends AbstractProcessor { }
初始化注解处理器

基类 AbstractProcessor 中已经有了 init 方法,我们可以 Override 一下,加入一下我们想要的属性。

  • Messager:用于编译期输出日志,方便排查问题
  • Context:java 处理环境的上下文,方便后续从上下文中获取对象
  • Names:命名工具类,方便后续构造名称,比如构造方法名称
// 用于输出日志
private Messager messager;
// java 处理环境的上下文
private Context context;
// 命名工具类
private Names names;

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
    this.messager = processingEnv.getMessager();
    this.context = ((JavacProcessingEnvironment) processingEnv).getContext();
    this.names = Names.instance(context);
}
核心处理逻辑

核心处理逻辑如下:

  • 获取所有使用 @Getter 注解的元素(可能是类,也可能是变量)
  • 遍历所有元素,找到所有变量定义,并为变量添加 getter 方法
  • 最终返回 true,表示后续处理器不需要继续处理了
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    messager.printMessage(Diagnostic.Kind.NOTE, "进入 GetterAnnotationProcessor.process() 方法");

    // 正常来说,annotations 里只有一个注解,就是 com.lombok.annotation.Getter
    for (TypeElement typeElement : annotations) {
        // 获取所有具备此注解的元素
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(typeElement);
        set.forEach(element -> {
            // 遍历元素,并找到其抽象语法树
            JCTree jcTree = JavacTrees.instance(processingEnv).getTree(element);
            // 访问类定义的时候,查找类中的变量定义,并给这些变量添加 getter 方法
            jcTree.accept(new TreeTranslator() {
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                    // 遍历所有定义
                    for (JCTree tree : jcClassDecl.defs) {
                        if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
                            // 对所有变量添加 getter 方法定义
                            JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
                            messager.printMessage(Diagnostic.Kind.NOTE, "正在为 " + jcVariableDecl.getName() + " 添加 getter 方法");
                            jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
                        }
                    }

                    super.visitClassDef(jcClassDecl);
                }
            });
        });
    }

    // 返回 true,表示后续处理器不会继续处理了
    return true;
}
生成 setter/getter 方法

生成逻辑如下:

  • 根据找到的变量定义作为参数
  • 先创建语句:return this.变量名;
  • 然后使用块包裹,即加大括号:{\r\n return this.name; \r\n}
  • 最后,创建方法:
    • 使用 public 修饰
    • 方法名为 get/setXXX
    • 返回值为变量类型
private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
    TreeMaker treeMaker = TreeMaker.instance(context);
    ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
    // return this.name;
    statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName())));
    // {\r\n return this.name; \r\n}
    JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
    // public String getName() {
    //    return this.name;
    //}
    return treeMaker.MethodDef(treeMaker.Modifiers(Flags.PUBLIC), getGetMethodName(jcVariableDecl.getName()), jcVariableDecl.vartype,
            List.nil(), List.nil(), List.nil(), body, null);
}

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

编译注解处理器

解决找不到 JCTree 等类的问题

JCTree、TreeMaker 等类在 JDK 的 tools.jar 包中,默认情况下,如果没有配置好 CLASSPATH,则可能会没有包含此 jar 到 CLASSPATH 下。
可在 pom.xml 中引入此系统依赖:

<dependencies>
  <!-- https://mvnrepository.com/artifact/com.sun/tools -->
  <dependency>
    <groupId>com.sun</groupId>
    <artifactId>tools</artifactId>
    <version>8</version>
    <scope>system</scope>
    <systemPath>${java.home}/../lib/tools.jar</systemPath>
  </dependency>
</dependencies>
注解处理器 发现机制

定义好上述注解处理器后,调用方如何才能调用注解?

  • 思路 1::javac -processor 选项:指定要运行的注解处理器的名称,会绕过默认的搜索进程
  • 思路 2:javac -processorpath 选项:指定搜索注解处理器的路径
  • 思路 3:SPI 机制,即在 resources 下创建 META-INF/services/javax.annotation.processing.Processor 文件,文件内容是 com.lombok.processor.GetterAnnotationProcessor

image.png

解决编译报错问题

编译 processor 模块(clean install)时,会编译报错:Provider com.lombok.processor.GetterAnnotationProcessor not found

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.5.1:compile (default-compile) on project processor: Compilation failure
[ERROR] 服务配置文件不正确, 或构造处理程序对象javax.annotation.processing.Processor: Provider com.lombok.processor.GetterAnnotationProcessor not found时抛出异常错误

报错原因:编译器由于 SPI 机制,发现了注解处理器,就在编译期间执行了注解处理器,但是注解处理器本身还没编译,所以找不到注解处理器。
解决办法:processor 模块只编译,不需要执行注解处理器。在 processor 模块的 pom.xml 中指定编译器参数:-proc:none 即可。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.5.1</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
    </configuration>
    <executions>
        <execution>
            <id>default-compile</id>
            <configuration>
                <compilerArgument>-proc:none</compilerArgument>
            </configuration>
        </execution>
        <execution>
            <id>compile-project</id>
            <phase>compile</phase>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

demo 模块 - 调用方

引入注解处理器模块

想要使用 @Getter 注解,则需要引入 processor 模块。

<dependency>
    <groupId>org.example</groupId>
    <artifactId>processor</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

编写代码,使用 @Getter 注解

@Getter
public class User {
    private String name;
    private int age;
}

编译 demo 模块,并调试 processor 模块

在 processor 模块执行 mvn clean install 后,选中 demo 模块,执行如下即可调试:【或者执行 mvnDebug clean install,然后在 IDEA 中添加 Remote JVM Debug 也可调试】
image.png
调试效果如下:
image.png
执行结果:
image.png
至此,@Getter 注解就完成自动生成 getter 方法的任务啦!😎

注意:默认情况下,IDEA 如果自动编译的话,可能不会走上述注解处理器的逻辑。

进阶 - Lombok 源码解读

下面,我们对 Lombok 源码解读下。看看和我们自己写的 demo 有啥不同。
比对结论:Lombok 考虑得更加全面,功能更加完善,且使用了自定义类加载加载 lombok 文件。

注解处理器

Lombok 也是使用 SPI 来暴露注解处理器的:
image.png
其提供了两个注解处理器:
1、AnnotationProcessor 注解处理器:这个注解处理器实际上会委派给 lombok.core.AnnotationProcessor 注解处理器。

public static class AnnotationProcessor extends AbstractProcessor {
    private final AbstractProcessor instance = createWrappedInstance();
    
    @Override public Set<String> getSupportedOptions() {
        return instance.getSupportedOptions();
    }
    
    @Override public Set<String> getSupportedAnnotationTypes() {
        return instance.getSupportedAnnotationTypes();
    }
    
    @Override public SourceVersion getSupportedSourceVersion() {
        return instance.getSupportedSourceVersion();
    }
    
    @Override public void init(ProcessingEnvironment processingEnv) {
        disableJava9SillyWarning();
        AstModificationNotifierData.lombokInvoked = true;
        instance.init(processingEnv);
        super.init(processingEnv);
    }
    
    // sunapi suppresses javac's warning about using Unsafe; 'all' suppresses eclipse's warning about the unspecified 'sunapi' key. Leave them both.
    // Yes, javac's definition of the word 'all' is quite contrary to what the dictionary says it means. 'all' does NOT include 'sunapi' according to javac.
    @SuppressWarnings({"sunapi", "all"})
    private void disableJava9SillyWarning() {
        // JVM9 complains about using reflection to access packages from a module that aren't exported. This makes no sense; the whole point of reflection
        // is to get past such issues. The only comment from the jigsaw team lead on this was some unspecified mumbling about security which makes no sense,
        // as the SecurityManager is invoked to check such things. Therefore this warning is a bug, so we shall patch java to fix it.
        
        try {
            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            Unsafe u = (Unsafe) theUnsafe.get(null);
            
            Class<?> cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
            Field logger = cls.getDeclaredField("logger");
            u.putObjectVolatile(cls, u.staticFieldOffset(logger), null);
        } catch (Throwable t) {
            // We shall ignore it; the effect of this code failing is that the user gets to see a warning they remove with various --add-opens magic.
        }
    }
    
    @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return instance.process(annotations, roundEnv);
    }
    
    @Override public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
        return instance.getCompletions(element, annotation, member, userText);
    }
    
    private static AbstractProcessor createWrappedInstance() {
        ClassLoader cl = Main.getShadowClassLoader();
        try {
            Class<?> mc = cl.loadClass("lombok.core.AnnotationProcessor");
            return (AbstractProcessor) mc.getDeclaredConstructor().newInstance();
        } catch (Throwable t) {
            if (t instanceof Error) throw (Error) t;
            if (t instanceof RuntimeException) throw (RuntimeException) t;
            throw new RuntimeException(t);
        }
    }
}

上面 createWrappedInstance() 方法,使用的自定义的类加载,去加载了 lombok.core.AnnotationProcessor 类,这个类只能被 lombok 类加载器加载。具体类如下:
image.png
2、ClaimingProcessor 注解处理器:这个处理器啥也不干,做最后收尾,表示后续处理器都不要处理了。

@SupportedAnnotationTypes("lombok.*")
	public static class ClaimingProcessor extends AbstractProcessor {
		@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
			return true;
		}
		
		@Override public SourceVersion getSupportedSourceVersion() {
			return SourceVersion.latest();
		}
	}

Debug 注解处理器

既然我们知道了 Lombok 注解处理器的位置,直接打断点看下:选择模块,右键 Debug Maven,即可进入 Debug 模式
1、首先进入 launch\AnnotationProcessor 的 process 方法
image.png
2、然后进入 core\AnnotationProcessor 的 process 方法
image.png
3、然后使用 LombokProcessor 来处理
image.png
4、处理完毕后,进入 ClaimProcessor 的 process 处理方法
image.png
然后继续后面的轮次,直到结束!😎

参考

1.Java SE 6 — 更好的 JPA、更好的 JAXB 和更好的批注处理
2.Java注解编译期处理AbstractProcessor详解
3.用了那么久的Lombok,你知道它的原理么?-阿里云开发者社区
4.IDEA结合maven进行编译期注解处理器调试_如何在idea中调试编译期源码-CSDN博客
5.https://www.geeksforgeeks.org/abstract-syntax-tree-ast-in-java/

更多文章

1.消除代码冗长神器 - Lombok | 入门-CSDN博客
2.消除代码冗长神器 - Lombok | 安装-CSDN博客
3.消除代码冗长神器 - Lombok | val/var 本地变量声明-CSDN博客
4.消除代码冗长神器 - Lombok | @Setter/@Getter 生成 setter/getter 方法-CSDN博客
5.消除代码冗长神器 - Lombok | @EqualsAndHashCode/@ToString注解详解-CSDN博客
6.消除代码冗长神器 - Lombok | @NoArgsConstructor/@RequiredArgsConstructor/@AllArgsConstructor 构造方法-CSDN博客
7.消除代码冗长神器 - Lombok | @Data 通用 POJO 注解-CSDN博客
8.消除代码冗长神器 - Lombok | @With 不可变的 setter 方法-CSDN博客
9.消除代码冗长神器 - Lombok | @Value 不可变 entity-CSDN博客
10.消除代码冗长神器 - Lombok | @Cleanup 自动清理资源-CSDN博客
11.消除代码冗长神器 - Lombok | @Log/@Slf4j 创建日志对象-CSDN博客
12.消除代码冗长神器 - Lombok | @Builder注解 - Builder 设计模式快速实现
13.消除代码冗长神器 - Lombok | @NonNull 判空逻辑
14.消除代码冗长神器 - Lombok | @SneakyThrows 检查型异常转抛
15.消除代码冗长神器 - Lombok | @Synchronized 锁
16.消除代码冗长神器 - Lombok | 试验性注解
17.消除代码冗长神器 - Lombok | 进阶用法 - 全局配置 & 去除Lombok
18.消除代码冗长神器 - Lombok | 高阶用法 -> 手写 @Getter 注解

文章来源:https://blog.csdn.net/u012383839/article/details/135534326
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。