Project-1 提供注解处理器

  1. pom.xml ```xml <?xml version=”1.0” encoding=”UTF-8”?> <project xmlns=”http://maven.apache.org/POM/4.0.0

    1. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    2. 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
  1. <dependencies>
  2. <!-- 自动生成SPI文件 -->
  3. <dependency>
  4. <groupId>com.google.auto.service</groupId>
  5. <artifactId>auto-service</artifactId>
  6. <version>1.0-rc5</version>
  7. </dependency>
  8. <!-- AST工具包 -->
  9. <dependency>
  10. <groupId>com.sun</groupId>
  11. <artifactId>tools</artifactId>
  12. <version>1.8</version>
  13. <scope>system</scope>
  14. <systemPath>${tools.path}</systemPath>
  15. <optional>true</optional>
  16. </dependency>
  17. </dependencies>

  1. 说明:AST 抽象语法树, JDK lib包中的tools.jar中提供了操作抽象语法树的API
  2. 2. 定义注解,要求@Retention(RetentionPolicy.SOURCE)
  3. ```java
  4. @Target(ElementType.TYPE)
  5. @Retention(RetentionPolicy.SOURCE)
  6. public @interface GetterSetter {
  7. }
  1. 定义注解处理器, 通过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() {

    1. return SourceVersion.latest(); // 指定可处理的java版本

    }

    @Override public synchronized void init(ProcessingEnvironment processingEnv) {

    1. super.init(processingEnv); // 注解处理器框架提供的上下文环境
    2. // 额外初始化一些资源操作对象
    3. this.messager = processingEnv.getMessager();
    4. this.javacTrees = JavacTrees.instance(processingEnv);
    5. Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
    6. this.treeMaker = TreeMaker.instance(context);
    7. 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()) {

      1. return true;

      }

      print(“开始处理”); print(“处理注解:” + annotations); messager.printMessage(Diagnostic.Kind.NOTE, “roundEnv ->” + roundEnv);

      for (TypeElement annotation : annotations) {

      1. Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(annotation);
      2. for (Element element : elements) {
      3. print("处理注解:" + annotation + "开始处理元素:" + element);
      4. // 存放所有变量,也可以list,不过这里不太方便
      5. Set<JCTree.JCVariableDecl> paramSet = new HashSet<>();
      6. // 获取元素对象的抽象语法树
      7. JCTree jcTree = javacTrees.getTree(element);
      8. // 标准的访问者模式
      9. jcTree.accept(new TreeTranslator() {
      10. /**
      11. * 遍历每个变量
      12. */
      13. @Override
      14. public void visitVarDef(JCTree.JCVariableDecl jcVariableDecl) {
      15. paramSet.add(jcVariableDecl); // 存起来
      16. super.visitVarDef(jcVariableDecl);
      17. }
      18. @Override
      19. public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
      20. super.visitClassDef(jcClassDecl); // 会先走 visitVarDef
      21. // 转换
      22. List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
      23. for (JCTree.JCVariableDecl jcVariableDecl : paramSet) {
      24. jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
      25. }
      26. print("------------------------\n本次处理参数:");
      27. jcVariableDeclList.forEach((var) -> print(var));
      28. print("------------------------");
      29. // 获取所有getter/setter方法
      30. List<JCTree.JCMethodDecl> jcMethodDeclList = generateMethod(jcVariableDeclList);
      31. // 添加到类中
      32. for (JCTree.JCMethodDecl jcMethodDecl : jcMethodDeclList) {
      33. jcClassDecl.defs = jcClassDecl.defs.append(jcMethodDecl);
      34. }
      35. }
      36. });
      37. }

      }

      print(“处理结束”);

      // 指示处理器改变过代码,需要修改语法树的内容 // 编译器会重新回到解析及填充符号表的过程 return true; }

      private List generateMethod(List jcVariableDeclList) {

      List jcMethodDeclList = List.nil();

      for (JCTree.JCVariableDecl jcVariableDecl : jcVariableDeclList) {

      1. JCTree.JCMethodDecl getter = getGetter(jcVariableDecl);
      2. JCTree.JCMethodDecl setter = getSetter(jcVariableDecl);
      3. jcMethodDeclList = jcMethodDeclList.append(getter);
      4. 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(

      1. treeMaker.Select(
      2. // this -> name
      3. treeMaker.Ident(names.fromString("this")), jcVariableDecl.name)
      4. )

      ); // 代码块 JCTree.JCBlock block = treeMaker.Block(0, jcReturns); // 0 -> 没有任何修饰符

      return treeMaker.MethodDef(

      1. modifiers, // 访问修饰符
      2. methodName, // 方法名
      3. jcVariableDecl.vartype, // 返回类型
      4. List.nil(), // 泛型列表
      5. List.nil(), // 参数列表
      6. List.nil(), // 异常列表
      7. block, // 方法体
      8. 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(

      1. treeMaker.Modifiers(Flags.PARAMETER), // 变量类型 - 参数
      2. jcVariableDecl.getName(), // 参数名
      3. jcVariableDecl.vartype, // 参数类型
      4. null // ...

      ); // 形参

      param.pos = jcVariableDecl.pos; // 坑,1小时

      List jcAssigns = List.of(treeMaker.Exec(

      1. // =
      2. treeMaker.Assign(
      3. // this.xxx, xxx
      4. treeMaker.Select(treeMaker.Ident(names.fromString("this")), names.fromString(jcVariableDecl.name.toString())), treeMaker.Ident(param.name)
      5. )

      ));// 代码块

      JCTree.JCBlock block = treeMaker.Block(0, jcAssigns); // 0 -> 没有任何修饰符

      return treeMaker.MethodDef(

      1. modifiers, // 访问修饰符
      2. methodName, // 方法名
      3. rtnType, // 返回类型
      4. List.nil(), // 泛型列表
      5. List.of(param), // 参数列表
      6. List.nil(), // 异常列表
      7. block, // 方法体
      8. 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()); } }

  1. 说明:@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
  2. <a name="PLfP2"></a>
  3. # Project-2 使用编译时注解
  4. 1. pom.xml中引入project-1jar
  5. ```xml
  6. <dependencies>
  7. <dependency>
  8. <groupId>org.example</groupId>
  9. <artifactId>processor-core</artifactId>
  10. <version>1.0-SNAPSHOT</version>
  11. </dependency>
  12. </dependencies>
  1. 使用注解 ```java package org.example.invoke;

import org.example.core.GetterSetter;

import java.util.List;

@GetterSetter public class User {

  1. private int age;
  2. private String name;
  3. private List<String> stringList;

}

  1. 3. 编译后的User.java
  2. ```java
  3. package org.example.invoke;
  4. import java.util.List;
  5. public class User {
  6. private int age;
  7. private String name;
  8. private List<String> stringList;
  9. public User() {
  10. }
  11. public String getName() {
  12. return this.name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public List<String> getStringList() {
  18. return this.stringList;
  19. }
  20. public void setStringList(List<String> stringList) {
  21. this.stringList = stringList;
  22. }
  23. public int getAge() {
  24. return this.age;
  25. }
  26. public void setAge(int age) {
  27. this.age = age;
  28. }
  29. }
  1. 测试 ```java package org.example.invoke;

public class TestGetter {

  1. public static void main(String[] args) {
  2. User user = new User();
  3. user.setName("zs");
  4. System.out.println(user.getName());
  5. }

}

``` https://gitee.com/java-base-devin/processor-core.git
https://gitee.com/java-base-devin/processor-invoke.git