Project-1 提供注解处理器
pom.xml ```xml <?xml version=”1.0” encoding=”UTF-8”?> <project xmlns=”http://maven.apache.org/POM/4.0.0“
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0 org.example processor-core 1.0-SNAPSHOT UTF-8 1.8 1.8 ${env.JAVA_HOME}/lib/tools.jar
<dependencies>
<!-- 自动生成SPI文件 -->
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc5</version>
</dependency>
<!-- AST工具包 -->
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${tools.path}</systemPath>
<optional>true</optional>
</dependency>
</dependencies>
说明:AST 抽象语法树, JDK lib包中的tools.jar中提供了操作抽象语法树的API。
2. 定义注解,要求@Retention(RetentionPolicy.SOURCE)
```java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface GetterSetter {
}
- 定义注解处理器, 通过tools.jar中操作抽象语法树的API,给类增加get/set方法 ```java package org.example.core;
import com.google.auto.service.AutoService; import com.sun.tools.javac.api.JavacTrees; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.TypeTag; 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.Name; import com.sun.tools.javac.util.Names;
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.HashSet; import java.util.Set;
/**
- 还可以通过-processor参数来执行编译时需要附带的注解处理器,多个逗号分隔。
还可以使用-XprintRounds 和 -XprintProcessorInfo 参数来查看注解处理器运作的详细信息。 */ @AutoService(Processor.class) //简化了SPI的配置 @SupportedAnnotationTypes(“org.example.core.GetterSetter”) public class GetterSetterProcessor extends AbstractProcessor {
private Messager messager; // 消息
private JavacTrees javacTrees; // 抽象语法树工具
private TreeMaker treeMaker; // 操作节点工具 - 核心工具
private Names names; // 处理名称工具
@Override public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest(); // 指定可处理的java版本
}
@Override public synchronized void init(ProcessingEnvironment processingEnv) {
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);
}
/**
- 核心处理逻辑 *
- @param annotations 感兴趣的注解
- @param roundEnv 当前轮次(Round)的抽象语法树节点,所有节点类型(ElementKind)
@return 是否改动过节点 */ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 当前处理器已处理过,不在进行处理 if (roundEnv.processingOver()) {
return true;
}
print(“开始处理”); print(“处理注解:” + annotations); messager.printMessage(Diagnostic.Kind.NOTE, “roundEnv ->” + roundEnv);
for (TypeElement annotation : annotations) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(annotation);
for (Element element : elements) {
print("处理注解:" + annotation + "开始处理元素:" + element);
// 存放所有变量,也可以list,不过这里不太方便
Set<JCTree.JCVariableDecl> paramSet = new HashSet<>();
// 获取元素对象的抽象语法树
JCTree jcTree = javacTrees.getTree(element);
// 标准的访问者模式
jcTree.accept(new TreeTranslator() {
/**
* 遍历每个变量
*/
@Override
public void visitVarDef(JCTree.JCVariableDecl jcVariableDecl) {
paramSet.add(jcVariableDecl); // 存起来
super.visitVarDef(jcVariableDecl);
}
@Override
public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
super.visitClassDef(jcClassDecl); // 会先走 visitVarDef
// 转换
List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
for (JCTree.JCVariableDecl jcVariableDecl : paramSet) {
jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
}
print("------------------------\n本次处理参数:");
jcVariableDeclList.forEach((var) -> print(var));
print("------------------------");
// 获取所有getter/setter方法
List<JCTree.JCMethodDecl> jcMethodDeclList = generateMethod(jcVariableDeclList);
// 添加到类中
for (JCTree.JCMethodDecl jcMethodDecl : jcMethodDeclList) {
jcClassDecl.defs = jcClassDecl.defs.append(jcMethodDecl);
}
}
});
}
}
print(“处理结束”);
// 指示处理器改变过代码,需要修改语法树的内容 // 编译器会重新回到解析及填充符号表的过程 return true; }
private List
generateMethod(List jcVariableDeclList) { List
jcMethodDeclList = List.nil(); for (JCTree.JCVariableDecl jcVariableDecl : jcVariableDeclList) {
JCTree.JCMethodDecl getter = getGetter(jcVariableDecl);
JCTree.JCMethodDecl setter = getSetter(jcVariableDecl);
jcMethodDeclList = jcMethodDeclList.append(getter);
jcMethodDeclList = jcMethodDeclList.append(setter);
}
return jcMethodDeclList; }
private JCTree.JCMethodDecl getGetter(JCTree.JCVariableDecl jcVariableDecl) { JCTree.JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC); //访问修饰符
Name methodName = names.fromString(“get” + upper(jcVariableDecl.name.toString())); // 方法名
List
jcReturns = List.of(treeMaker.Return( treeMaker.Select(
// this -> name
treeMaker.Ident(names.fromString("this")), jcVariableDecl.name)
)
); // 代码块 JCTree.JCBlock block = treeMaker.Block(0, jcReturns); // 0 -> 没有任何修饰符
return treeMaker.MethodDef(
modifiers, // 访问修饰符
methodName, // 方法名
jcVariableDecl.vartype, // 返回类型
List.nil(), // 泛型列表
List.nil(), // 参数列表
List.nil(), // 异常列表
block, // 方法体
null // 默认值,注解才有
); }
private JCTree.JCMethodDecl getSetter(JCTree.JCVariableDecl jcVariableDecl) { JCTree.JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC); //访问修饰符
JCTree.JCPrimitiveTypeTree rtnType = treeMaker.TypeIdent(TypeTag.VOID); // 方法返回类型
Name methodName = names.fromString(“set” + upper(jcVariableDecl.name.toString())); // 方法名
JCTree.JCVariableDecl param = treeMaker.VarDef(
treeMaker.Modifiers(Flags.PARAMETER), // 变量类型 - 参数
jcVariableDecl.getName(), // 参数名
jcVariableDecl.vartype, // 参数类型
null // ...
); // 形参
param.pos = jcVariableDecl.pos; // 坑,1小时
List
jcAssigns = List.of(treeMaker.Exec( // =
treeMaker.Assign(
// this.xxx, xxx
treeMaker.Select(treeMaker.Ident(names.fromString("this")), names.fromString(jcVariableDecl.name.toString())), treeMaker.Ident(param.name)
)
));// 代码块
JCTree.JCBlock block = treeMaker.Block(0, jcAssigns); // 0 -> 没有任何修饰符
return treeMaker.MethodDef(
modifiers, // 访问修饰符
methodName, // 方法名
rtnType, // 返回类型
List.nil(), // 泛型列表
List.of(param), // 参数列表
List.nil(), // 异常列表
block, // 方法体
null // 默认值,注解才有
); }
private String upper(String str) { char[] cs = str.toCharArray(); cs[0] -= 32; return String.valueOf(cs); }
private void print(Object msg) { System.out.println(msg.toString()); } }
说明:@AutoService(Processor.class) 是简化SPI的配置,编译当前类(GetterSetterProcessor.java)的时候会自动生成SPI的配置信息。<br />在classpath下生成如下路径和文件:\META-INF\services\(目录) + 接口全限定名 <br />\META-INF\services\javax.annotation.processing.Processor<br />文件的内容为接口的实现类:org.example.core.GetterSetterProcessor
<a name="PLfP2"></a>
# Project-2 使用编译时注解
1. pom.xml中引入project-1的jar
```xml
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>processor-core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
- 使用注解 ```java package org.example.invoke;
import org.example.core.GetterSetter;
import java.util.List;
@GetterSetter public class User {
private int age;
private String name;
private List<String> stringList;
}
3. 编译后的User.java
```java
package org.example.invoke;
import java.util.List;
public class User {
private int age;
private String name;
private List<String> stringList;
public User() {
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getStringList() {
return this.stringList;
}
public void setStringList(List<String> stringList) {
this.stringList = stringList;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
- 测试 ```java package org.example.invoke;
public class TestGetter {
public static void main(String[] args) {
User user = new User();
user.setName("zs");
System.out.println(user.getName());
}
}
```
https://gitee.com/java-base-devin/processor-core.git
https://gitee.com/java-base-devin/processor-invoke.git