注解处理器

参考深入拆解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 {
}
View Code

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;
    }
}
View Code

该注解处理器主要是对属性有没有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) {

    }
}
View Code
package foo;

public class Foo1 {

    @CheckGetter
    int v;
}
View Code

然后进行编译,之后你会看到这个错误信息

 

 

 我本地目录

 

 

 

 

 

生成源文件

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();
}
View Code

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();
    }
}
View Code

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;
    }
}
View Code

 

 

 

信息补充

package foo; // PackageElementclass
Foo { // TypeElement
int a; // VariableElement
static int b; // VariableElement
Foo () {} // ExecutableElement
void setA ( // ExecutableElement
int newA // VariableElement ) {}}
View Code

编译过程:

     源文件解析成抽象语法树-->调用已注册的注解处理器(如果生成新的源文件,回到上一级记录解析)-->生成字节码

process方法参数

     注解处理器所能处理的注解类型

   当前轮生成的抽象语法树

 

posted @ 2020-11-20 14:33  默默行走  阅读(495)  评论(0编辑  收藏  举报