手把手实现一个lombok
一、lombok原理 JSR269
什么是JSR ?
JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。
有超过300的JSR。一些更为明显的JSRs包括:
JSR269 可插拔的注解处理API,其原理如下:
二、实现步骤
1.工程与环境依赖
- 配置maven 插件,pom.xml 编译参数
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
<compilerArgs>
<arg>-parameters</arg>
<arg>-proc:none</arg>
<arg>-XDignore.symbol.file</arg>
</compilerArgs>
<compilerArguments>
<bootclasspath>
${java.home}/lib/rt.jar${path.separator}${java.home}/lib/jce.jar${path.separator}${java.home}/../lib/tools.jar
</bootclasspath>
</compilerArguments>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
注意细节
- Lombok项目本身要加 编译 参数 ,防止编译处理器无法实例化:-proc:none
- 要添加编译 类路径 bootclasspath: 指定tool.jar
- 在测试的时候 要构建一个新的工程,用一个新的IDEA窗口打开
2.注解处理器
- 打印编译信息
- 编写注解处理器,实现
AbstractProcessor
- 基于SPI指定处理器的路径 :工程/resources/META-INF/services/javax.annotation.processing.Processor
- 打印消息的时候,maven 用System.out, idea用
Messager
类 - 我当时用ide 编译时一直报错 ,我没太在意使用mvn 命令处理的, mvn命令没错误
Error:java: 服务配置文件不正确, 或构造处理程序对象javax.annotation.processing.Processor: Provider com.wfg.HelloProcessor not found时抛出异常错误
在项目编写的目录下操作下面的命令
mvn compile 编译
mvn package 打包
mvn install 安装到本地仓库
mvn exec:java -Dexec.mainClass="hello.HelloWorld" 运行main类,此处用不到这个命令
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.wfg.MyHello")
public class HelloProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
System.out.println("这是我的第一人编译注释处理器");
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,"这是我的处理器");
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
}
3.注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface MyHello {
}
4.jcTree 修改语法
- 构建一个hello world 语句
import com.sun.tools.javac.api.JavacTrees;
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.Names;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("org.myLombok.Hello")
public class HelloProcessor extends AbstractProcessor {
private JavacTrees javacTrees; // 获取 JcTree
private TreeMaker treeMaker; // 构建生成 jcTree
private Names names;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
System.out.println("这是我的第一人编译注释处理器");
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,"这是我的处理器");
javacTrees = JavacTrees.instance(processingEnv);// 语法树
Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
this.treeMaker = TreeMaker.instance(context);
super.init(processingEnv);
this.names = Names.instance(context);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
annotations.stream()
.flatMap(t->roundEnv.getElementsAnnotatedWith(t).stream())
.forEach(t->{
JCTree tree = javacTrees.getTree(t);
// 基于访问者设计模式 去修改方法
tree.accept(new TreeTranslator(){
@Override
public void visitMethodDef(JCTree.JCMethodDecl tree) {
// System.out.println("hello world");
JCTree.JCStatement sysout = treeMaker.Exec(
treeMaker.Apply(
List.nil(),
select("System.out.println"),
List.of(treeMaker.Literal("hello world!")) // 方法中的内容
)
);
// 覆盖原有的语句块
tree.body.stats=tree.body.stats.append(sysout);
super.visitMethodDef(tree);
}
});
});
return true;
}
private JCTree.JCFieldAccess select(JCTree.JCExpression selected, String expressive) {
return treeMaker.Select(selected, names.fromString(expressive));
}
private JCTree.JCFieldAccess select(String expressive) {
String[] exps = expressive.split("\\.");
JCTree.JCFieldAccess access = treeMaker.Select(ident(exps[0]), names.fromString(exps[1]));
int index = 2;
while (index < exps.length) {
access = treeMaker.Select(access, names.fromString(exps[index++]));
}
return access;
}
private JCTree.JCIdent ident(String name) {
return treeMaker.Ident(names.fromString(name));
}
}
4.新建模块依赖我们这个jar包进行编译
/**
* @author wufagang
* @description
* @date 2023年04月18日 20:46
*/
@MyHello
public class HelloDemo {
}
编译后到效果
5.源码调试
测试模块引入下面的插件
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>