Lombok是一个开源项目,通过添加注解,自动生成一些方法,但却降低了源代码文件的可读性和完整性
使用方法参考
本文通过小demo实现自制Lombok实现通过注解,生成自己的方法
pom.xml
<modelVersion>4.0.0</modelVersion>
<groupId>com.jun</groupId>
<artifactId>lombokDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>lombokDemo</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--tools.jar在jdk的lib下面,因此scope是system,${java.home}表示jre位置-->
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
@Target({ElementType.TYPE})//对类的注解
@Retention(RetentionPolicy.SOURCE)//只在编译期起作用
public @interface MyLombok { }
@SupportedAnnotationTypes("com.jun.MyLombok")//该处理器需要处理的注解
@SupportedSourceVersion(SourceVersion.RELEASE_8)//该处理器支持的源码版本
public class MyLombokProcessor extends AbstractProcessor {
//在编译期打log用
private Messager messager;
//提供了待处理的抽象语法树
private JavacTrees trees;
//封装了创建AST节点的一些方法
private TreeMaker treeMaker;
//提供了创建标识符的方法
private Names names;
//通过ProcessingEnvironment来获取编译阶段的一些环境信息
@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 synchronized boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//利用roundEnv的getElementsAnnotatedWith方法过滤出被MyLombok注解标记的类,并存入set
Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(MyLombok.class);
//遍历set并生成jCTree这个语法树
set.forEach(element -> {
JCTree jcTree = trees.getTree(element);
jcTree.accept(new TreeTranslator() {
//这个方法处理遍历语法树得到的类定义部分jcClassDecl
@Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
//保存类的成员变量
//List 是 package com.sun.tools.javac.util
List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
//遍历jcTree的所有成员(包括成员变量和成员函数和构造函数),过滤出其中的成员变量,并添加进jcVariableDeclList
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, jcVariableDecl.getName() + " has been processed");
//将jcVariableDeclList的所有变量转换成需要添加的方法,并添加进jcClassDecl的成员中
jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetMethodDecl(jcVariableDecl));
jcClassDecl.defs = jcClassDecl.defs.prepend(makeSetMethodDecl(jcVariableDecl));
});
//调用默认的遍历方法遍历处理后的jcClassDecl
//利用上面的TreeTranslator去处理jcTree
super.visitClassDef(jcClassDecl);
}
});
});
return true;
}
private JCTree.JCMethodDecl makeGetMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
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), getNewMethodName(jcVariableDecl.getName()), jcVariableDecl.vartype, List.nil(), List.nil(), List.nil(), 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()));
}
private JCTree.JCMethodDecl makeSetMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
// 添加调用语句" this.setXXX(xxx); "
// statements.append(
// treeMaker.Exec(
// treeMaker.Apply(
// List.nil(),
// treeMaker.Select(
// treeMaker.Ident(names.fromString("this")),
// getNewMethodName(jcVariableDecl.getName())
// ),
// List.of(treeMaker.Ident(jcVariableDecl.getName()))
// )
// )
// );
// this.XXX=xxx
statements.append(
treeMaker.Exec(treeMaker.Assign(treeMaker.Select(treeMaker.Ident(names.fromString("this")), jcVariableDecl.getName()), treeMaker.Ident(jcVariableDecl.getName())))
);
// 添加返回语句 " return this; "
// 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),// public方法
setNewMethodName(jcVariableDecl.getName()),// 方法名称
null,// 方法返回的类型
List.nil(),// 泛型参数
List.nil(),// 方法参数
List.nil(),// throw表达式
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()));
}
}
//@MyLombok
public class Test {
private String name;
private int age;
public Test(String name,int age) {
this.name = name;
this.age = age;
}
public String toString(){
return String.format("{ '%s':%s ,'%s':%d}", "name", this.name, "age", this.age);
}
public static void main(String[] args) {
Test app = new Test("大明",12);
System.out.println(app.toString());
}
}
# 创建文件夹
mkdir classes
# 拷贝 %JAVA_HOME%\lib\tools.jar 至项目
# 编译注解及其处理器
javac -encoding utf-8 -cp tools.jar com/jun/MyLombok*.java -d classes/
# 编译Test
javac -cp classes -d classes -processor com.jun.MyLombokProcessor com/jun/Test.java
# 反编译Test.class
javap -p classes/com/jun/Test.class
# 运行Test
java -cp classes com.jun.Test
pom.xml
<modelVersion>4.0.0</modelVersion>
<groupId>com.jun</groupId>
<artifactId>myLombokTest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>myLombokTest</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- lombokDemo.jar 放到resources/lib下,也可以放到私服-->
<dependency>
<groupId>com.jun</groupId>
<artifactId>lombokDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/lombokDemo.jar</systemPath>
</dependency>
</dependencies>
@MyLombok
public class Test {
private String name;
private int age;
public Test(String name,int age) {
this.name = name;
this.age = age;
}
public String toString(){
return String.format("{ '%s':%s ,'%s':%d}", "name", this.name, "age", this.age);
}
public static void main(String[] args) {
Test app = new Test("大明",12);
System.out.println(app.toString());
}
}