lombok,get,set
1.目录树
2.创建Getter、Setter注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Getter {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
public @interface Setter {
}
3.创建Getter、Setter对应的注解处理器
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.*;
import javax.annotation.processing.*;
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感兴趣
@SupportedAnnotationTypes("com.hinotoyk.jsr269.Getter")
//支持的版本,使用1.8就写这个
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class GetterProcessor extends AbstractProcessor {
// 编译时期输入日志的
private Messager messager;
// 将Element转换为JCTree的工具,提供了待处理的抽象语法树
private JavacTrees trees;
// 封装了创建AST节点的一些方法
private TreeMaker treeMaker;
// 提供了创建标识符的方法
private Names names;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.messager = processingEnv.getMessager();
this.trees = 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) {
// 获取被@Getter注解标记的所有元素(这个元素可能是类、变量、方法等等)
Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Getter.class);
set.forEach(element -> {
// 将Element转换为JCTree
JCTree jcTree = trees.getTree(element);
jcTree.accept(new TreeTranslator() {
/***
* JCTree.Visitor有很多方法,我们可以通过重写对应的方法,(从该方法的形参中)来获取到我们想要的信息:
* 如: 重写visitClassDef方法, 获取到类的信息;
* 重写visitMethodDef方法, 获取到方法的信息;
* 重写visitVarDef方法, 获取到变量的信息;
* @param jcClassDecl
*/
@Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
//创建一个变量语法树节点的List
List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
// 遍历defs,即是类定义的详细语句,包括字段、方法的定义等等
for (JCTree tree : jcClassDecl.defs) {
if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
}
}
// 对于变量进行生成方法的操作
jcVariableDeclList.forEach(jcVariableDecl -> {
messager.printMessage(Diagnostic.Kind.NOTE, "get " + jcVariableDecl.getName() + " has been processed");
treeMaker.pos = jcVariableDecl.pos;
//类里的前面追加生成的Getter方法
jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
});
super.visitClassDef(jcClassDecl);
}
});
});
//我们有修改过AST,所以返回true
return true;
}
private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
/***
* JCStatement:声明语法树节点,常见的子类如下
* JCBlock:语句块语法树节点
* JCReturn:return语句语法树节点
* JCClassDecl:类定义语法树节点
* JCVariableDecl:字段/变量定义语法树节点
* JCMethodDecl:方法定义语法树节点
* JCModifiers:访问标志语法树节点
* JCExpression:表达式语法树节点,常见的子类如下
* JCAssign:赋值语句语法树节点
* JCIdent:标识符语法树节点,可以是变量,类型,关键字等等
*/
ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
statements.append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName())));
JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
return treeMaker.MethodDef(
treeMaker.Modifiers(Flags.PUBLIC),//mods:访问标志
getNewMethodName(jcVariableDecl.getName()),//name:方法名
jcVariableDecl.vartype,//restype:返回类型
List.nil(),//typarams:泛型参数列表
List.nil(),//params:参数列表
List.nil(),//thrown:异常声明列表
body,//方法体
null);
}
private Name getNewMethodName(Name name) {
String s = name.toString();
return names.fromString("get" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
}
}
Settter注解处理器和Getter的大同小异,只是生成set方法的步骤不太一样,详细可查看openjdk-7 TreeMaker
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.code.Type;
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.*;
import javax.annotation.processing.*;
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;
@SupportedAnnotationTypes("com.hinotoyk.jsr269.Setter")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class SetterProcessor extends AbstractProcessor {
private Messager messager;
private JavacTrees trees;
private TreeMaker treeMaker;
private Names names;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.messager = processingEnv.getMessager();
this.trees = 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) {
Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Setter.class);
set.forEach(element -> {
JCTree jcTree = trees.getTree(element);
jcTree.accept(new TreeTranslator() {
@Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
for (JCTree tree : jcClassDecl.defs) {
if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
}
}
jcVariableDeclList.forEach(jcVariableDecl -> {
messager.printMessage(Diagnostic.Kind.NOTE, "set " + jcVariableDecl.getName() + " has been processed");
treeMaker.pos = jcVariableDecl.pos;
jcClassDecl.defs = jcClassDecl.defs.prepend(makeSetterMethodDecl(jcVariableDecl));
});
super.visitClassDef(jcClassDecl);
}
});
});
return true;
}
private JCTree.JCMethodDecl makeSetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
statements.append(
treeMaker.Exec(
treeMaker.Assign(
treeMaker.Select(
treeMaker.Ident(names.fromString("this")),
names.fromString(jcVariableDecl.name.toString())
),
treeMaker.Ident(names.fromString(jcVariableDecl.name.toString()))
)
)
);
JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
// 生成入参
JCTree.JCVariableDecl param = treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER), jcVariableDecl.getName(),jcVariableDecl.vartype, null);
List<JCTree.JCVariableDecl> paramList = List.of(param);
return treeMaker.MethodDef(
treeMaker.Modifiers(Flags.PUBLIC), // 方法限定值
setNewMethodName(jcVariableDecl.getName()), // 方法名
treeMaker.Type(new Type.JCVoidType()), // 返回类型
List.nil(),
paramList, // 入参
List.nil(),
body,
null
);
}
private Name setNewMethodName(Name name) {
String s = name.toString();
return names.fromString("set" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length()));
}
}
4.创建测试Class
import com.hinotoyk.jsr269.Getter;
import com.hinotoyk.jsr269.Setter;
@Setter
@Getter
public class TestDemo {
private String name;
public static void main(String[] args) {
TestDemo testDemo = new TestDemo();
testDemo.setName("yk");
System.out.println(testDemo.getName());
}
}
5.运行
(1)第一步:创建.sh文件,做相应的编译设置
(2)第二步:执行.sh文件
6.查看效果
参考:
深入拆解 Java 虚拟机 - 郑雨迪 - 极客时间
JVM系列之:你知道Lombok是如何工作的吗 - 掘金 (juejin.cn)
从JSR269到Lombok,学习注解处理器Annotation Processor Tool - 掘金 (juejin.cn)