java注解之注解处理器

摘要

 Java5中提供了apt工具来进行编译期的注解处理。apt是命令行工具,与之配套的是一套描述“程序在编译时刻的静态结构”的API:Mirror API(com.sun.mirror.*)。通过Mirror API可以获取到被注解的Java类型元素的信息,从而提供自定义的处理逻辑。具体的处理工具交给apt来处理。编写注解处理器的核心是两个类:注解处理器(com.sun.mirror.apt.AnnotationProcessor)、注解处理器工厂(com.sun.mirror.apt.AnnotationProcessorFactory)。apt工具在完成注解处理后,会自动调用javac来编译处理完成后的源代码。然而,apt工具是oracle提供的私有实现(在JDK开发包的类库中是不存在的)。
 在JDK6中,将注解处理器这一功能进行了规范化,形成了java.annotation.processing的API包,Mirror API则进行封装,形成javax.lang.model包。注解处理器的开发进行了简化,不再单独使用apt工具,而将此功能集成到了javac命令中。
 在JSK1.8之后移除了APT,使用插入式注解处理API(JSR 269)提供一套标准API来处理Annotations(JSR 175),实际上JSR 269不仅仅用来处理Annotation,我觉得更强大的功能是它建立了Java 语言本身的一个模型,它把method, package, constructor, type, variable, enum, annotation等Java语言元素映射为Types和Elements(两者有什么区别?), 从而将Java语言的语义映射成为对象, 我们可以在javax.lang.model包下面可以看到这些类. 所以我们可以利用JSR 269提供的API来构建一个功能丰富的元编程(metaprogramming)环境. JSR 269用Annotation Processor在编译期间而不是运行期间处理Annotation, Annotation Processor相当于编译器的一个插件,所以称为插入式注解处理.如果Annotation Processor处理Annotation时(执行process方法)产生了新的Java代码,编译器会再调用一次Annotation Processor,如果第二次处理还有新代码产生,就会接着调用Annotation Processor,直到没有新代码产生为止.每执行一次process()方法被称为一个"round",这样整个Annotation
processing过程可以看作是一个round的序列.

注解处理器可以在注解生命周期全程使用

  1. 定义一个注解(maven工程)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)//CLASS,RUNTIME都可以
    public @interface CreateFile {
}
  1. 使用此注解
@CreateFile
public class main{
}
  1. 创建注解处理器

@SupportedAnnotationTypes(value = "com.ycj.customannotation.annotation.CreateFile")
@SupportedSourceVersion(value = SourceVersion.RELEASE_11)
public class MyProcessor extends AbstractProcessor {

    private Types types;
    private Elements elements;
    private Filer filer;
    private Messager messager;

    /**
     * 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,
     * init()会被注解处理工具调用,以processingEnv作为参数
     * ProcessingEnvironment提供了一些实用得工具
     * Types
     * Elements
     * Filer
     * Messager
     * @param processingEnv 提供给processor 用来访问工具框架得环境
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        types=processingEnv.getTypeUtils();
        elements= processingEnv.getElementUtils();
        filer = processingEnv.getFiler();
        messager = processingEnv.getMessager();
    }

    /**
     * 在这个方法里面你必须指定那些注解应该被注解处理器注册。注意,它的返回值是一个String集合,
     * 包含了你的注解处理器想要处理的注解类型的全称。
     * @return
     */
   /* @Override
    public Set<String> getSupportedAnnotationTypes() {
        LinkedHashSet<String> annotations = new LinkedHashSet<String>();
        //annotations.add("com.ycj.customannotation.annotation.CreateFile");
        annotations.add(CreateFile.class.getCanonicalName());//等同上面
        return annotations;
    }

    *//**
     * 指定注解可以使用的版本
     * @return
     *//*
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }*/

    /*
    注意:可以使用注解@SupportedSourceVersion(value=SourceVersion.RELEASE_7)
        @SupportedAnnotationTypes({
           // Set of full qullified annotation type names
            “com.ycj.jcf.collection.annotation.CreateFile”
         })
         来代替getSupportedAnnotationTypes()和getSupportedSourceVersion()
     */


    /**
     * 注解处理器扫描,解析,处理注解的核心方法,你可以在这个方法里面编写实现扫描,处理注解,生成java文件。
     * @param annotations 请求处理的注解类型
     * @param roundEnv 你可以查询被特定注解标注的元素
     * @return 如果返回false,则这些注解未声明并且可能要求后续Processor处理它们。
     *         如果返回true,则这些注解已声明并且不要求后续Processor处理它们。
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        messager.printMessage(Diagnostic.Kind.NOTE,"日志开始-----------------------");
        Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(CreateFile.class);
        for (Element element : elementsAnnotatedWith) {
            if(element.getKind()== ElementKind.CLASS){
                TypeElement typeElement= (TypeElement) element;
                PackageElement packageElement = elements.getPackageOf(element);
                String packagePath = packageElement.getQualifiedName().toString();
                String className =typeElement.getSimpleName().toString();
                try {
                    JavaFileObject sourceFile = filer.createSourceFile(packagePath + "." + className + "_ViewBinding", typeElement);
                    Writer writer = sourceFile.openWriter();
                        writer.write("package "+packagePath+";\n");
                        writer.write("import "+packagePath+"."+className+";\n");

                        writer.write("public class "+className+"_ViewBinding"+"{\n");
                        writer.write("\n");
                        writer.append("         public   "+className+"  targe;\n");
                        writer.write("\n");
                        writer.append("}");
                        writer.flush();
                        writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        messager.printMessage(Diagnostic.Kind.NOTE,"日志结束-----------------------");
        return true;
    }
}
  1. 注册注解处理器
    在这里插入图片描述
    在javax.annotation.processing.Processor中填入注解处理器的全限定名com.ycj.customannotation.processor.MyProcessor
    这个文件中一行代表一个注解处理器
  2. maven插件配置
    因为注解处理器必须先编译了才能使用,但是直接编译整个工程会默认启动注解处理器,这时就会报错,所以在编译时设置不执行注解处理器
    在这里插入图片描述
  3. idea配置
    在这里插入图片描述
  4. 编译工程注解处理器启动
    注解处理器编译完成之后 就会启动 ,生成的文件在generated下
    在这里插入图片描述

如果对上一部不很理解可以先不编译main类,也就是不先创建,在编译完整个工程后再创建main类使用@CreateFile ,在单独编译recompile
在这里插入图片描述
同样可以看到生成的文件
日志信息在这里
在这里插入图片描述
8. 扩展
可以看到上面注册注解处理器的方式比较繁琐,
Google开发了一个AutoService,用来生成 META-INF/services/javax.annotation.processing.Processor 文件的,你只需要在你定义的注解处理器上添加 @AutoService(Processor.class) 就可以了.
在这里插入图片描述

@AutoService(Processor.class)
@SupportedAnnotationTypes(value = "com.ycj.customannotation.annotation.CreateFile")
@SupportedSourceVersion(value = SourceVersion.RELEASE_11)
public class MyProcessor extends AbstractProcessor {}

不过我并没有成功,可能需要在android studio上进行,毕竟这个谷歌开发给安卓用的,那位大神在idea上成功了,请留言一下,十分感谢!
9. 处理器的属性方法介绍

  1. Messager:主要起一个日志的作用 ,为注解处理器提供了一种报告错误消息,警告信息和其他消息的方式。它不是注解处理器开发者的日志工具,是用来给那些使用了你的注解处理器的第三方开发者显示信息的。Kind.ERROR,就是错误信息的级别。
  2. Elements:一个用来处理Elemnet的工具类
    Element代表程序中的元素,比如包、类、属性、方法。
  3. Types:一个用来处理TypeMirror的工具类
  4. Filer:可以用来创建文件。

猜你喜欢

转载自blog.csdn.net/YCJ_xiyang/article/details/82965873