参考深入拆解Java虚拟机 纯照搬
注解处理器不是运行时处理器,而是编译时注解处理器
1、创建两个maven项目,引入一个依赖
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc6</version>
</dependency>
该配置主要是为了后续注册注解处理器的
2、编写自定义注解
package foo; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.FIELD}) @Retention(RetentionPolicy.SOURCE) public @interface CheckGetter { }
3、编写注解处理器
package process; import java.util.Set; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.util.ElementFilter; import javax.tools.Diagnostic.Kind; import com.google.auto.service.AutoService; import foo.CheckGetter; @AutoService(Processor.class) @SupportedAnnotationTypes("foo.CheckGetter") @SupportedSourceVersion(SourceVersion.RELEASE_8) public class CheckGetterProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // TODO: annotated ElementKind.FIELD for (TypeElement annotatedClass : ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(CheckGetter.class))) { for (VariableElement field : ElementFilter.fieldsIn(annotatedClass.getEnclosedElements())) { if (!containsGetter(annotatedClass, field.getSimpleName().toString())) { processingEnv.getMessager().printMessage(Kind.ERROR, String.format("getter class not found for '%s.%s'.", annotatedClass.getSimpleName(), field.getSimpleName())); } } } Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(CheckGetter.class); for (Element element:elements){ ElementKind kind = element.getKind(); if(kind == ElementKind.FIELD){ VariableElement variableElement = (VariableElement)element; TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement(); if(!containsGetter(typeElement,variableElement.getSimpleName().toString())){ processingEnv.getMessager().printMessage(Kind.ERROR, String.format("getter field not found for '%s.%s'.", typeElement.getSimpleName(), variableElement.getSimpleName())); } } } return true; } private static boolean containsGetter(TypeElement typeElement, String name) { String getter = "get" + name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); for (ExecutableElement executableElement : ElementFilter.methodsIn(typeElement.getEnclosedElements())) { if (!executableElement.getModifiers().contains(Modifier.STATIC) && executableElement.getSimpleName().toString().equals(getter) && executableElement.getParameters().isEmpty()) { return true; } } return false; } }
该注解处理器主要是对属性有没有get方法做验证
4、将项目进行clean install 之后你会在target目录下看到注册用的配置文件
META-INF/services/javax.annotation.processing.Processor --里边写着处理器的全路径名称
5、测试
在一个新的项目中添加类
package foo; @CheckGetter public class Foo { int a; static int b; Foo() { } void setA(int newA) { } }
package foo; public class Foo1 { @CheckGetter int v; }
然后进行编译,之后你会看到这个错误信息
我本地目录
生成源文件
1、添加自定义注解
package foo; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Adapt { Class<?> value(); }
2、添加注解处理器
package process; import com.google.auto.service.AutoService; import java.io.*; import java.util.Set; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.*; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.tools.JavaFileObject; import javax.tools.Diagnostic.Kind; @AutoService(Processor.class) @SupportedAnnotationTypes("foo.Adapt") @SupportedSourceVersion(SourceVersion.RELEASE_8) public class AdaptProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (TypeElement annotation : annotations) { if (!"foo.Adapt".equals(annotation.getQualifiedName().toString())) { continue; } ExecutableElement targetAsKey = getExecutable(annotation, "value"); for (ExecutableElement annotatedMethod : ElementFilter.methodsIn(roundEnv.getElementsAnnotatedWith(annotation))) { if (!annotatedMethod.getModifiers().contains(Modifier.PUBLIC)) { processingEnv.getMessager().printMessage(Kind.ERROR, "@Adapt on non-public method"); continue; } if (!annotatedMethod.getModifiers().contains(Modifier.STATIC)) { // TODO support non-static methods continue; } TypeElement targetInterface = getAnnotationValueAsTypeElement(annotatedMethod, annotation, targetAsKey); if (targetInterface.getKind() != ElementKind.INTERFACE) { processingEnv.getMessager().printMessage(Kind.ERROR, "@Adapt with non-interface input"); continue; } TypeElement enclosingType = getTopLevelEnclosingType(annotatedMethod); createAdapter(enclosingType, annotatedMethod, targetInterface); } } return true; } private void createAdapter(TypeElement enclosingClass, ExecutableElement annotatedMethod, TypeElement targetInterface) { PackageElement packageElement = (PackageElement) enclosingClass.getEnclosingElement(); String packageName = packageElement.getQualifiedName().toString(); String className = enclosingClass.getSimpleName().toString(); String methodName = annotatedMethod.getSimpleName().toString(); String adapterName = className + "_" + methodName + "Adapter"; ExecutableElement overriddenMethod = getFirstNonDefaultExecutable(targetInterface); try { Filer filer = processingEnv.getFiler(); JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + adapterName, new Element[0]); try (PrintWriter out = new PrintWriter(sourceFile.openWriter())) { out.println("package " + packageName + ";"); out.println("import " + targetInterface.getQualifiedName() + ";"); out.println(); out.println("public class " + adapterName + " implements " + targetInterface.getSimpleName() + " {"); out.println(" @Override"); out.println(" public " + overriddenMethod.getReturnType() + " " + overriddenMethod.getSimpleName() + formatParameter(overriddenMethod, true) + " {"); out.println(" return " + className + "." + methodName + formatParameter(overriddenMethod, false) + ";"); out.println(" }"); out.println("}"); } } catch (IOException e) { throw new RuntimeException(e); } } private ExecutableElement getExecutable(TypeElement annotation, String methodName) { for (ExecutableElement method : ElementFilter.methodsIn(annotation.getEnclosedElements())) { if (methodName.equals(method.getSimpleName().toString())) { return method; } } processingEnv.getMessager().printMessage(Kind.ERROR, "Incompatible @Adapt."); return null; } private ExecutableElement getFirstNonDefaultExecutable(TypeElement annotation) { for (ExecutableElement method : ElementFilter.methodsIn(annotation.getEnclosedElements())) { if (!method.isDefault()) { return method; } } processingEnv.getMessager().printMessage(Kind.ERROR, "Target interface should declare at least one non-default method."); return null; } private TypeElement getAnnotationValueAsTypeElement(ExecutableElement annotatedMethod, TypeElement annotation, ExecutableElement annotationFunction) { TypeMirror annotationType = annotation.asType(); for (AnnotationMirror annotationMirror : annotatedMethod.getAnnotationMirrors()) { if (processingEnv.getTypeUtils().isSameType(annotationMirror.getAnnotationType(), annotationType)) { AnnotationValue value = annotationMirror.getElementValues().get(annotationFunction); if (value == null) { processingEnv.getMessager().printMessage(Kind.ERROR, "Unknown @Adapt target"); continue; } TypeMirror targetInterfaceTypeMirror = (TypeMirror) value.getValue(); return (TypeElement) processingEnv.getTypeUtils().asElement(targetInterfaceTypeMirror); } } processingEnv.getMessager().printMessage(Kind.ERROR, "@Adapt should contain target()"); return null; } private TypeElement getTopLevelEnclosingType(ExecutableElement annotatedMethod) { TypeElement enclosingType = null; Element enclosing = annotatedMethod.getEnclosingElement(); while (enclosing != null) { if (enclosing.getKind() == ElementKind.CLASS) { enclosingType = (TypeElement) enclosing; } else if (enclosing.getKind() == ElementKind.PACKAGE) { break; } enclosing = enclosing.getEnclosingElement(); } return enclosingType; } private String formatParameter(ExecutableElement method, boolean includeType) { StringBuilder builder = new StringBuilder(); builder.append('('); String separator = ""; for (VariableElement parameter : method.getParameters()) { builder.append(separator); if (includeType) { builder.append(parameter.asType()); builder.append(' '); } builder.append(parameter.getSimpleName()); separator = ", "; } builder.append(')'); return builder.toString(); } }
3、进行maven打包,装到本地仓库
4、测试
package foo; import java.util.function.IntBinaryOperator; public class Bar { @Adapt(IntBinaryOperator.class) public static int add(int a, int b){ return a + b; } }
信息补充
package foo; // PackageElementclass Foo { // TypeElement int a; // VariableElement static int b; // VariableElement Foo () {} // ExecutableElement void setA ( // ExecutableElement int newA // VariableElement ) {}}
编译过程:
源文件解析成抽象语法树-->调用已注册的注解处理器(如果生成新的源文件,回到上一级记录解析)-->生成字节码
process方法参数
注解处理器所能处理的注解类型
当前轮生成的抽象语法树