• 注解处理器


    参考深入拆解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方法参数

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

       当前轮生成的抽象语法树

  • 相关阅读:
    Access操作必须使用一个可更新的查询
    SAP资料学习好地方
    Access关键词大全
    WPF零散笔记
    WPF:如何实现单实例的应用程序(Single Instance)
    WPF应用程序启动显示图片资源
    Drawable、Bitmap、Canvas和Paint的关系以及部分使用方法
    C#中一种可调用的异常处理方法
    easyui datagrid 点击列表头排序出现错乱的原因
    easyui datagrid 没数据时显示滚动条的解决方法
  • 原文地址:https://www.cnblogs.com/nihaofenghao/p/14010748.html
Copyright © 2020-2023  润新知