目录
需求描述
开发一个扫描所有注解信息的脚本程序,希望在编译期对代码进行扫描,如果注解书写不规范则使编译失败。
本次的需求刚好用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 annotations, RoundEnvironment roundEnv);
process()方法有一个boolean类型的返回值,若返回false,表示本轮注解未声明并且可能要求后续其它的Processor处理它们;若返回true,则代表这些注解已经声明并且不要求后续Processor来处理它们。
开发
我们暂时不需要考虑其他方法的实现,这些操作我们也不需要,我们只需要指定需要处理的注解,指定编译的Java版本,并且实现核心逻辑process()方法就可以了。
@SupportedAnnotationTypes({"XXX"})//指定注解的类型@SupportedSourceVersion(SourceVersion.RELEASE_8)//指定支持的Java版本,这里是Java8public class VUAnnotationProcessor extends AbstractProcessor { // annotations为要求处理的注解类型 // roundEnv 中包含了当前和上一轮的环境信息,从中我们可以获取扫描出来的注解实例 @Override public boolean process(Set 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包里面,不要让它干扰到需要扫描的项目里。
第二,在这个单独的项目里,引入声明,表示,这个项目不要用注解处理器扫描。
org.apache.maven.plugins maven-compiler-plugin 2.0.2 default-compile -proc:none
这样才能让这个类编译成功,并且让它在其他项目编译的时候生效。
参考
https://blog.zenfery.cc/archives/78.html