java注解之注解处理器

£神魔★判官ぃ 2022-05-08 00:48 586阅读 0赞

摘要

 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都可以

    1. public @interface CreateFile {

    }

  2. 使用此注解

    @CreateFile
    public class main{
    }

  3. 创建注解处理器

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

    1. private Types types;
    2. private Elements elements;
    3. private Filer filer;
    4. private Messager messager;
    5. /**
    6. * 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,
    7. * init()会被注解处理工具调用,以processingEnv作为参数
    8. * ProcessingEnvironment提供了一些实用得工具
    9. * Types
    10. * Elements
    11. * Filer
    12. * Messager
    13. * @param processingEnv 提供给processor 用来访问工具框架得环境
    14. */
    15. @Override
    16. public synchronized void init(ProcessingEnvironment processingEnv) {
    17. types=processingEnv.getTypeUtils();
    18. elements= processingEnv.getElementUtils();
    19. filer = processingEnv.getFiler();
    20. messager = processingEnv.getMessager();
    21. }
    22. /**
    23. * 在这个方法里面你必须指定那些注解应该被注解处理器注册。注意,它的返回值是一个String集合,
    24. * 包含了你的注解处理器想要处理的注解类型的全称。
    25. * @return
    26. */

    /* @Override

    1. public Set<String> getSupportedAnnotationTypes() {
    2. LinkedHashSet<String> annotations = new LinkedHashSet<String>();
    3. //annotations.add("com.ycj.customannotation.annotation.CreateFile");
    4. annotations.add(CreateFile.class.getCanonicalName());//等同上面
    5. return annotations;
    6. }
    7. *//**
    8. * 指定注解可以使用的版本
    9. * @return
    10. *//*
    11. @Override
    12. public SourceVersion getSupportedSourceVersion() {
    13. return SourceVersion.latestSupported();
    14. }*/
    15. /*
    16. 注意:可以使用注解@SupportedSourceVersion(value=SourceVersion.RELEASE_7)
    17. @SupportedAnnotationTypes({
    18. // Set of full qullified annotation type names
    19. “com.ycj.jcf.collection.annotation.CreateFile”
    20. })
    21. 来代替getSupportedAnnotationTypes()和getSupportedSourceVersion()
    22. */
  1. /**
  2. * 注解处理器扫描,解析,处理注解的核心方法,你可以在这个方法里面编写实现扫描,处理注解,生成java文件。
  3. * @param annotations 请求处理的注解类型
  4. * @param roundEnv 你可以查询被特定注解标注的元素
  5. * @return 如果返回false,则这些注解未声明并且可能要求后续Processor处理它们。
  6. * 如果返回true,则这些注解已声明并且不要求后续Processor处理它们。
  7. */
  8. @Override
  9. public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
  10. messager.printMessage(Diagnostic.Kind.NOTE,"日志开始-----------------------");
  11. Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(CreateFile.class);
  12. for (Element element : elementsAnnotatedWith) {
  13. if(element.getKind()== ElementKind.CLASS){
  14. TypeElement typeElement= (TypeElement) element;
  15. PackageElement packageElement = elements.getPackageOf(element);
  16. String packagePath = packageElement.getQualifiedName().toString();
  17. String className =typeElement.getSimpleName().toString();
  18. try {
  19. JavaFileObject sourceFile = filer.createSourceFile(packagePath + "." + className + "_ViewBinding", typeElement);
  20. Writer writer = sourceFile.openWriter();
  21. writer.write("package "+packagePath+";\n");
  22. writer.write("import "+packagePath+"."+className+";\n");
  23. writer.write("public class "+className+"_ViewBinding"+"{\n");
  24. writer.write("\n");
  25. writer.append(" public "+className+" targe;\n");
  26. writer.write("\n");
  27. writer.append("}");
  28. writer.flush();
  29. writer.close();
  30. } catch (IOException e) {
  31. e.printStackTrace();
  32. }
  33. }
  34. }
  35. messager.printMessage(Diagnostic.Kind.NOTE,"日志结束-----------------------");
  36. return true;
  37. }
  38. }
  1. 注册注解处理器
    在这里插入图片描述
    在javax.annotation.processing.Processor中填入注解处理器的全限定名com.ycj.customannotation.processor.MyProcessor
    这个文件中一行代表一个注解处理器
  2. maven插件配置
    因为注解处理器必须先编译了才能使用,但是直接编译整个工程会默认启动注解处理器,这时就会报错,所以在编译时设置不执行注解处理器
    在这里插入图片描述
  3. idea配置
    在这里插入图片描述
  4. 编译工程注解处理器启动
    注解处理器编译完成之后 就会启动 ,生成的文件在generated下
    在这里插入图片描述

如果对上一部不很理解可以先不编译main类,也就是不先创建,在编译完整个工程后再创建main类使用@CreateFile ,在单独编译recompile
在这里插入图片描述
同样可以看到生成的文件
日志信息在这里
在这里插入图片描述

  1. 扩展
    可以看到上面注册注解处理器的方式比较繁琐,
    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上成功了,请留言一下,十分感谢!

  1. 处理器的属性方法介绍

  2. Messager:主要起一个日志的作用 ,为注解处理器提供了一种报告错误消息,警告信息和其他消息的方式。它不是注解处理器开发者的日志工具,是用来给那些使用了你的注解处理器的第三方开发者显示信息的。Kind.ERROR,就是错误信息的级别。

  3. Elements:一个用来处理Elemnet的工具类
    Element代表程序中的元素,比如包、类、属性、方法。
  4. Types:一个用来处理TypeMirror的工具类
  5. Filer:可以用来创建文件。

发表评论

表情:
评论列表 (有 0 条评论,586人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java 注解处理器

    ![apt][] 前面,介绍了 Java5.0 引入的注解,现在来介绍注解处理机制。 注解处理机制 = 注解 + 注解处理器 注解处理器和注解一般共同组成 `Java L

    相关 Java 注解及自定义注解处理器

    注解介绍 注解,也被称为元数据(所谓的元数据,就是描述数据的数据)。 所以注解的主要作用就是给指定代码一些描述信息。这些指定代码可以是一个类、一个方法或者是一个属性。