java 命名代码检查-注解处理器
命名代码检查
根据 <Java 语言规范 (第 3 版) > 中第 6.8 节的要求, Java 程序命名应当符合下列格式的书写规范:
- 类 (或接口) : 符合驼式命名法, 首字母大写.
- 方法 : 符合驼式命名法, 首字母小写
- 字段 :
- 类或实例变量 : 符合驼式命名法 , 首字母小写
- 常量 : 要求全部有大写字母或下划线构成, 并且第一个字符不能是下划线.
要通过注解处理器的 API 实现一个编译器插件 , 首先需要了解这组 API 的基本知识. 我们实现注解处理器的代码需要继承抽象类 javax.annotation.processing.AbstractProcessor , 这个抽象类中只有一个必须覆盖的 abstract 方法 : "process()" 它是 javac 编译器在执行注解处理器代码时需要调用的过程 , 我们可以从这个方法的第一个参数 'annotations' 中获取到此注解处理器所要求处理的注解集合, 从第二个参数 'roundEnv' 中访问到当前这个 round 中得语法树节点, 每个语法树节点在这里表示为一个 Element , 在 JDK1.6 新增的 javax.lang.model 包中定义了 16 类 Element , 包括了 Java 代码中最常用的元素,
如 : ' 包 (PACKAGE) , 枚举 (ENUM) , 类 (CLASS) , 注解 (ANNOTATION_TYPE) , 接口 (INTERFACE) , 枚举值 (ENUM_VARIABLE) , 字段 (FIELD) , 参数 (PARAMETER) , 本地变量 (LOCAL_VARIABLE) , 异常 (EXCEPTOIN_PARAMETER) , 方法 (METHOD) , 构造函数 (CONSTRUCTOR) 静态块语句 (STATIC_INIT , 即 static {} 块 ), 实例语句块 (INSTANCE_INIT, 即{} 块), 参数化类型 ( TYPE_PARAMETER) , 和未定义的其他语法树节点 (OTHER) ;
除了 process () 方法的传入参数之外, 还有一个很常用的实例变量 'processingEnv' , 它是 AbstractProcessor 中的一个 protected 变量, 在注解处理器代码可以直接访问到它, 它代表了注解处理器框架提供了一个上下文环境, 眼创建新的代码, 向编译器输出信息, 获取其他工具类等都需要用到这个实例变量,
注解处理器除了 process 方法及其参数外, 还有两个可以配合使用的 Annotations :@SupportedAnnotationTypes 和 @SupportedSourceVersion 前者代表这个注解处理器对哪些注解感兴趣, 可以使用 '*' 作为通配符表示对所有的感兴趣. 后者指出这个注解处理器可以处理哪些版本的 Java 代码.
每个注解处理器在运行的时候都是单例的. 如果不需要改变生成语法树的内容, process()方法就可以返回一个值为 false 的布尔值, 通知编译器这个 round 中得代码未发生变化, 无需构造新的 JavaCompiler 实例, 在这里只对程序命名进行检查 , 不需要改变语法树的内容, 因此 process() 方法的返回值都是 false
实例如下:
AbstractProcessor
package annotation.processing;import java.util.EnumSet;
import java.util.Set;import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementScanner6;
import javax.tools.Diagnostic.Kind;// 使用 * 表示支持所有的 Annotations
/**
- 以下代码出自 《深入理解 Java 虚拟机:JVM 高级特性与最佳实践》
/
@SupportedAnnotationTypes("")
// 表示只对 JDK 1.6 的 Java 源码感兴趣
@SupportedSourceVersion(value = SourceVersion.RELEASE_6)
public class NameCheckProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.nameCheck = new NameCheck(processingEnv);
}</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> NameCheck nameCheck; @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span> process(Set<? <span style="color: rgba(0, 0, 255, 1)">extends</span> TypeElement><span style="color: rgba(0, 0, 0, 1)"> annotations, RoundEnvironment roundEnv) { </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">roundEnv.processingOver()) { </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (Element element : roundEnv.getRootElements()) { nameCheck.check(element); } } </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 程序名称规范的编译器插件 如果程序命名不合规范,将会输出一个编译器的Warning信息 * * </span><span style="color: rgba(128, 128, 128, 1)">@author</span><span style="color: rgba(0, 128, 0, 1)"> kevin * </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> NameCheck { Messager messager </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> NameCheckScanner nameCheckScanner; </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> NameCheck(ProcessingEnvironment processingEnv) { messager </span>=<span style="color: rgba(0, 0, 0, 1)"> processingEnv.getMessager(); nameCheckScanner </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> NameCheckScanner(processingEnv); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 对Java程序明明进行检查,根据《Java语言规范(第3版)》6.8节的要求,Java程序命名应当符合下列格式: * <ul> * <li>类或接口:符合驼式命名法,首字母大写。 * <li>方法:符合驼式命名法,首字母小写。 * <li>字段: * <ul> * <li>类,实例变量:符合驼式命名法,首字母小写。 * <li>常量:要求全部大写 * </ul> * </ul> * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> element </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> check(Element element) { nameCheckScanner.scan(element); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 名称检查器实现类,继承了1.6中新提供的ElementScanner6<br> * 将会以Visitor模式访问抽象语法数中得元素 * * </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">class</span> NameCheckScanner <span style="color: rgba(0, 0, 255, 1)">extends</span> ElementScanner6<Void, Void><span style="color: rgba(0, 0, 0, 1)"> { Messager messager </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> NameCheckScanner(ProcessingEnvironment processingEnv) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.messager =<span style="color: rgba(0, 0, 0, 1)"> processingEnv.getMessager(); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 此方法用于检查Java类 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Void visitType(TypeElement e, Void p) { scan(e.getTypeParameters(), p); checkCamelCase(e, </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.visitType(e, p); </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 检查传入的Element是否符合驼式命名法,如果不符合,则输出警告信息 * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> e * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> b </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> checkCamelCase(Element e, <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> initialCaps) { String name </span>=<span style="color: rgba(0, 0, 0, 1)"> e.getSimpleName().toString(); </span><span style="color: rgba(0, 0, 255, 1)">boolean</span> previousUpper = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">boolean</span> conventional = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">int</span> firstCodePoint = name.codePointAt(0<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (Character.isUpperCase(firstCodePoint)) { previousUpper </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">initialCaps) { messager.printMessage(Kind.WARNING, </span>"名称:" +<span style="color: rgba(0, 0, 0, 1)"> name </span>+ " 应当以小写字母开头"<span style="color: rgba(0, 0, 0, 1)">, e); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">; } } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (Character.isLowerCase(firstCodePoint)) { </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (initialCaps) { messager.printMessage(Kind.WARNING, </span>"名称:" +<span style="color: rgba(0, 0, 0, 1)"> name </span>+ " 应当以大写字母开头"<span style="color: rgba(0, 0, 0, 1)">, e); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">; } } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> { conventional </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (conventional) { </span><span style="color: rgba(0, 0, 255, 1)">int</span> cp =<span style="color: rgba(0, 0, 0, 1)"> firstCodePoint; </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = Character.charCount(cp); i < name.length(); i +=<span style="color: rgba(0, 0, 0, 1)"> Character .charCount(cp)) { cp </span>=<span style="color: rgba(0, 0, 0, 1)"> name.codePointAt(i); </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (Character.isUpperCase(cp)) { </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (previousUpper) { conventional </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">; } previousUpper </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> { previousUpper </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; } } } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">conventional) { messager.printMessage(Kind.WARNING, </span>"名称:" +<span style="color: rgba(0, 0, 0, 1)"> name </span>+ "应当符合驼式命名法(Camel Case Names)"<span style="color: rgba(0, 0, 0, 1)">, e); } } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 检查方法命名是否合法 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Void visitExecutable(ExecutableElement e, Void p) { </span><span style="color: rgba(0, 0, 255, 1)">if</span> (e.getKind() ==<span style="color: rgba(0, 0, 0, 1)"> ElementKind.METHOD) { Name name </span>=<span style="color: rgba(0, 0, 0, 1)"> e.getSimpleName(); </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (name.contentEquals(e.getEnclosingElement() .getSimpleName())) { messager.printMessage(Kind.WARNING, </span>"一个普通方法:" +<span style="color: rgba(0, 0, 0, 1)"> name </span>+ " 不应当与类名重复,避免与构造函数产生混淆"<span style="color: rgba(0, 0, 0, 1)">, e); checkCamelCase(e, </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">); } } </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.visitExecutable(e, p); </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 检查变量是否合法 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Void visitVariable(VariableElement e, Void p) { </span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)"> 如果这个Variable是枚举或常量,则按大写命名检查,否则按照驼式命名法规则检查 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (e.getKind() ==<span style="color: rgba(0, 0, 0, 1)"> ElementKind.ENUM_CONSTANT </span>|| e.getConstantValue() != <span style="color: rgba(0, 0, 255, 1)">null</span> ||<span style="color: rgba(0, 0, 0, 1)"> heuristicallyConstant(e)) { checkAllCaps(e); } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> { checkCamelCase(e, </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">); } </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.visitVariable(e, p); </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 大写命名检查,要求第一个字符必须是大写的英文字母,其余部分可以下划线或大写字母 * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> e </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> checkAllCaps(VariableElement e) { String name </span>=<span style="color: rgba(0, 0, 0, 1)"> e.getSimpleName().toString(); </span><span style="color: rgba(0, 0, 255, 1)">boolean</span> conventional = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">int</span> firstCodePoint = name.codePointAt(0<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">Character.isUpperCase(firstCodePoint)) { conventional </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> { </span><span style="color: rgba(0, 0, 255, 1)">boolean</span> previousUnderscore = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">int</span> cp =<span style="color: rgba(0, 0, 0, 1)"> firstCodePoint; </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = Character.charCount(cp); i < name.length(); i +=<span style="color: rgba(0, 0, 0, 1)"> Character .charCount(cp)) { cp </span>=<span style="color: rgba(0, 0, 0, 1)"> name.codePointAt(i); </span><span style="color: rgba(0, 0, 255, 1)">if</span> (cp == (<span style="color: rgba(0, 0, 255, 1)">int</span>) '_'<span style="color: rgba(0, 0, 0, 1)">) { </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (previousUnderscore) { conventional </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">; } previousUnderscore </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> { previousUnderscore </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">Character.isUpperCase(cp) </span>&& !<span style="color: rgba(0, 0, 0, 1)">Character.isDigit(cp)) { conventional </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">; } } } } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">conventional) { messager.printMessage(Kind.WARNING, </span>"常量:" +<span style="color: rgba(0, 0, 0, 1)"> name </span>+ " 应该全部以大写字母" + "或下划线命名,并且以字符开头"<span style="color: rgba(0, 0, 0, 1)">, e); } } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 判断一个变量是否是常量 * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> e * </span><span style="color: rgba(128, 128, 128, 1)">@return</span> <span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> heuristicallyConstant(VariableElement e) { </span><span style="color: rgba(0, 0, 255, 1)">if</span> (e.getEnclosingElement().getKind() ==<span style="color: rgba(0, 0, 0, 1)"> ElementKind.INTERFACE) { </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (e.getKind() ==<span style="color: rgba(0, 0, 0, 1)"> ElementKind.FIELD </span>&&<span style="color: rgba(0, 0, 0, 1)"> e.getModifiers() .containsAll( EnumSet.of( javax.lang.model.element.Modifier.FINAL, javax.lang.model.element.Modifier.STATIC, javax.lang.model.element.Modifier.PUBLIC))) { </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">; } } }
}
测试代码:
BADLY_NAMED_CODE
package annotation.processing; public class BADLY_NAMED_CODE {</span><span style="color: rgba(0, 0, 255, 1)">enum</span><span style="color: rgba(0, 0, 0, 1)"> Colors { Red, Blue, Green; } </span><span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">int</span> FORTY_TWO =42<span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">int</span> NOT_A_CONSTANT =<span style="color: rgba(0, 0, 0, 1)"> FORTY_TWO; </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Badly_Named_Code() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> NOTcamelCASEmethodNAME() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">; }
}
执行过程如下:
bogon:Desktop mjorcen$ javac annotation/processing/BADLY_NAMED_CODE.java bogon:Desktop mjorcen$ javac annotation/processing/NameCheckProcessor.java bogon:Desktop mjorcen$ javac -processor annotation.processing.NameCheckProcessor annotation/processing/BADLY_NAMED_CODE.java 警告: 来自注释处理程序 'annotation.processing.NameCheckProcessor' 的受支持 source 版本 'RELEASE_6' 低于 -source '1.7' annotation/processing/BADLY_NAMED_CODE.java:2: 警告: 名称:BADLY_NAMED_CODE 应当符合驼式命名法(Camel Case Names) public class BADLY_NAMED_CODE { ^ annotation/processing/BADLY_NAMED_CODE.java:5: 警告: 常量:Red 应该全部以大写字母或下划线命名,并且以字符开头 Red, Blue, Green; ^ annotation/processing/BADLY_NAMED_CODE.java:5: 警告: 常量:Blue 应该全部以大写字母或下划线命名,并且以字符开头 Red, Blue, Green; ^ annotation/processing/BADLY_NAMED_CODE.java:5: 警告: 常量:Green 应该全部以大写字母或下划线命名,并且以字符开头 Red, Blue, Green; ^ annotation/processing/BADLY_NAMED_CODE.java:9: 警告: 名称:NOT_A_CONSTANT 应当以小写字母开头 public static int NOT_A_CONSTANT = FORTY_TWO; ^ 6 个警告
....
以上内容出自:
《深入理解 Java 虚拟机:JVM 高级特性与最佳实践》
将这个处理器注册到 Eclipse 上,我建立如下 META-INF 文件:
META-INF/services/javax.annotation.processing.Processor:
annotation.processing.NameCheckProcessor
这里只包含了处理器实现类的类名。我不确定你是否可以在这里列出多个处理器。
就这样。现在导出一个 jar 文件,并且在你需要用到这个处理器的工程上导入这个文件。
第二步:建立一个使用你的处理器的工程
In the properties for your new project go to Java Compiler -> Annotation Processing
Check the “Enable Project Specific Settings” and make sure “Enable annotation processing” is checked. I also changed the generated source directory to a name which didn’t start with a dot so it wouldn’t be hidden in the package explorer (files or directories which start with a dot are by default filtered away in eclipse).在工程的属性中找到 Java Compiler -> Annotation Processing 查看 “Enable Project Specific Settings” 确认 “Enable annotation processing” 被选中。为了不让他在包浏览器中隐藏,我还修改了 generated source directory ,去掉了开始的点(Eclipse 会将文件名以点开始的文件或文件夹过滤掉)。
然后,转到 Java Compiler -> Annotation Processing -> Factory Path 你可以在这里导入处理器的 jar 文件。不可以使用工程引用。
点击 “Advanced” 按钮,会显示一个对话框,列出了 META-INF/services/javax.annotation.processing.Processor 文件中的内容。选择它并按 OK。
第三步:Build!
完成了。这是在我的工程里显示的样子: