🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 更复杂的处理器 当你创建用于 javac 注解处理器时,你不能使用 Java 的反射特性,因为你处理的是源代码,而并非是编译后的 class 文件。各种 mirror[^3 ] 解决这个问题的方法是,通过允许你在未编译的源代码中查看方法、字段和类型。 如下是一个用于提取类中方法的注解,所以它可以被抽取成为一个接口: ```java // annotations/ifx/ExtractInterface.java // javac-based annotation processing package annotations.ifx; import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.SOURCE) public @interface ExtractInterface { String interfaceName() default "-!!-"; } ``` **RetentionPolicy** 的值为 **SOURCE**,这是为了在提取类中的接口之后不再将注解信息保留在 class 文件中。接下来的测试类提供了一些公用方法,这些方法可以成为接口的一部分: ```java // annotations/ifx/Multiplier.java // javac-based annotation processing // {java annotations.ifx.Multiplier} package annotations.ifx; @ExtractInterface(interfaceName="IMultiplier") public class Multiplier { public boolean flag = false; private int n = 0; public int multiply(int x, int y) { int total = 0; for(int i = 0; i < x; i++) total = add(total, y); return total; } public int fortySeven() { return 47; } private int add(int x, int y) { return x + y; } public double timesTen(double arg) { return arg * 10; } public static void main(String[] args) { Multiplier m = new Multiplier(); System.out.println( "11 * 16 = " + m.multiply(11, 16)); } } ``` 输出为: ```java 11 * 16 = 176 ``` **Multiplier** 类(只能处理正整数)拥有一个 `multiply()` 方法,这个方法会多次调用私有方法 `add()` 来模拟乘法操作。` add()` 是私有方法,因此不能成为接口的一部分。其他的方法提供了语法多样性。注解被赋予 **IMultiplier** 的 **InterfaceName** 作为要创建的接口的名称。 这里有一个编译时处理器用于提取有趣的方法,并创建一个新的 interface 源代码文件(这个源文件将会在下一轮中被自动编译): ```java // annotations/ifx/IfaceExtractorProcessor.java // javac-based annotation processing package annotations.ifx; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.util.*; import java.util.*; import java.util.stream.*; import java.io.*; @SupportedAnnotationTypes( "annotations.ifx.ExtractInterface") @SupportedSourceVersion(SourceVersion.RELEASE_8) public class IfaceExtractorProcessor extends AbstractProcessor { private ArrayList<Element> interfaceMethods = new ArrayList<>(); Elements elementUtils; private ProcessingEnvironment processingEnv; @Override public void init( ProcessingEnvironment processingEnv) { this.processingEnv = processingEnv; elementUtils = processingEnv.getElementUtils(); } @Override public boolean process( Set<? extends TypeElement> annotations, RoundEnvironment env) { for(Element elem:env.getElementsAnnotatedWith( ExtractInterface.class)) { String interfaceName = elem.getAnnotation( ExtractInterface.class).interfaceName(); for(Element enclosed : elem.getEnclosedElements()) { if(enclosed.getKind() .equals(ElementKind.METHOD) && enclosed.getModifiers() .contains(Modifier.PUBLIC) && !enclosed.getModifiers() .contains(Modifier.STATIC)) { interfaceMethods.add(enclosed); } } if(interfaceMethods.size() > 0) writeInterfaceFile(interfaceName); } return false; } private void writeInterfaceFile(String interfaceName) { try( Writer writer = processingEnv.getFiler() .createSourceFile(interfaceName) .openWriter() ) { String packageName = elementUtils .getPackageOf(interfaceMethods .get(0)).toString(); writer.write( "package " + packageName + ";\n"); writer.write("public interface " + interfaceName + " {\n"); for(Element elem : interfaceMethods) { ExecutableElement method = (ExecutableElement)elem; String signature = " public "; signature += method.getReturnType() + " "; signature += method.getSimpleName(); signature += createArgList( method.getParameters()); System.out.println(signature); writer.write(signature + ";\n"); } writer.write("}"); } catch(Exception e) { throw new RuntimeException(e); } } private String createArgList( List<? extends VariableElement> parameters) { String args = parameters.stream() .map(p -> p.asType() + " " + p.getSimpleName()) .collect(Collectors.joining(", ")); return "(" + args + ")"; } } ``` **Elements** 对象实例 **elementUtils** 是一组静态方法的工具;我们用它来寻找 **writeInterfaceFile()** 中含有的包名。 `getEnclosedElements()`方法会通过指定的元素生成所有的“闭包”元素。在这里,这个类闭包了它的所有元素。通过使用 `getKind()` 我们会找到所有的 **public** 和 **static** 方法,并将其添加到 **interfaceMethods** 列表中。接下来 `writeInterfaceFile()` 使用 **interfaceMethods** 列表里面的值生成新的接口定义。注意,在 `writeInterfaceFile()` 使用了向下转型到 **ExecutableElement**,这使得我们可以获取所有的方法信息。**createArgList()** 是一个帮助方法,用于生成参数列表。 **Filer**是 `getFiler()` 生成的,并且是 **PrintWriter** 的一种实例,可以用于创建新文件。我们使用 **Filer** 对象,而不是原生的 **PrintWriter** 原因是,这个对象可以运行 **javac** 追踪你创建的新文件,这使得它可以在新一轮中检查新文件中的注解并编译文件。 如下是一个命令行,可以在编译的时候使用处理器: ```shell javac -processor annotations.ifx.IfaceExtractorProcessor Multiplier.java ``` 新生成的 **IMultiplier.java** 的文件,正如你通过查看上面处理器的 `println()` 语句所猜测的那样,如下所示: ```java package annotations.ifx; public interface IMultiplier { public int multiply(int x, int y); public int fortySeven(); public double timesTen(double arg); } ``` 这个类同样会被 **javac** 编译(在某一轮中),所以你会在同一个目录中看到 **IMultiplier.class** 文件。