记一次注解处理器的开发过程

需求描述

开发一个扫描所有注解信息的脚本程序,希望在编译期对代码进行扫描,如果注解书写不规范则使编译失败。

本次的需求刚好用AbstractProcessor可以满足,在这次需求中我学习到了注解处理器的开发,并且踩了一些坑,在这里记录下来,希望能够帮助其他人在开发的时候避免。

AbstractProcessor

简介

我们在网上搜索到的AbstractProcessor,大多用于Android开发时用到,但实际上注解处理器在我们的工作中仍然也能起到很大的作用。

注解的处理除了可以在运行时通过反射机制处理外,还可以在编译期进行处理。在编译期处理注解时,会处理到不再产生新的源文件为止,之后再对所有源文件进行编译。

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开发包的类库中是不存在的)。在 Java8中,已经移除了 APT 工具;在JDK6中,将注解处理器这一功能进行了规范化,形成了java.annotation.processing的API包,Mirror API则进行封装,形成javax.lang.model包。注解处理器的开发进行了简化,不再单独使用apt工具,而将此功能集成到了javac命令中。(当前开发使用的JDK版本一般都在6以上,故对apt工具不做研究)。

简单来讲,就是 我们可以通过使用 AbstractProcessor 可以在编译期处理所有指定的注解。

AbstractProcessor,是一个抽象类,该类实现了接口Processor。处理接口提供了一个核心处理方法process(),用于开发者实现自己的处理逻辑(用于处理先前round中产生的注解)。

boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);

process()方法有一个boolean类型的返回值,若返回false,表示本轮注解未声明并且可能要求后续其它的Processor处理它们;若返回true,则代表这些注解已经声明并且不要求后续Processor来处理它们。

开发

我们暂时不需要考虑其他方法的实现,这些操作我们也不需要,我们只需要指定需要处理的注解,指定编译的Java版本,并且实现核心逻辑process()方法就可以了。

@SupportedAnnotationTypes({"XXX"})//指定注解的类型
@SupportedSourceVersion(SourceVersion.RELEASE_8)//指定支持的Java版本,这里是Java8
public class VUAnnotationProcessor extends AbstractProcessor {

  
  	// annotations为要求处理的注解类型
  	// roundEnv 中包含了当前和上一轮的环境信息,从中我们可以获取扫描出来的注解实例
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
      //在这里书写处理逻辑
        return true;
    }
}

声明

这些开发完了之后,我们需要在resources目录下创建META-INF/services目录,在其下创建javax.annotation.processing.Processor文件,并在其中填写我们自定义的注解处理器的全类名。

坑:服务配置文件不正确

我们在开发完进行编译,很快会在控制台发现这行错误

错误: 服务配置文件不正确, 或构造处理程序对象javax.annotation.processing.Processor: XXX could not be instantiated: java.lang.NoClassDefFoundError: XXX 时抛出异常错误

为什么呢?因为注解处理器在启动的时候,注解处理器本身还没有编译,所以找不到。WTF。

那么,同学们,怎么才能解决这个“鸡生蛋,蛋生鸡”的问题呢?

第一,把这个类拆到单独的Jar包里面,不要让它干扰到需要扫描的项目里。

第二,在这个单独的项目里,引入声明,表示,这个项目不要用注解处理器扫描。

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <executions>
                    <execution>
                        <id>default-compile</id>
                        <configuration>
                            <compilerArgument>-proc:none</compilerArgument>
                            <source>1.8</source>
                            <target>1.8</target>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

这样才能让这个类编译成功,并且让它在其他项目编译的时候生效。

参考

https://blog.zenfery.cc/archives/78.html

posted @ 2019-08-07 16:34  CRUD_boys  阅读(2094)  评论(0编辑  收藏  举报