插入式注解处理器

时间:2020-12-15 20:35:12

通过Javac命令的 -processor 参数来执行编译时需要附带的注解处理器,如果有多个注解处理器的话,用逗号分隔。

还可以使用-XprintRounds和-XprintProcessorInfo参数来查看注解处理器运作的详细信息。

NameCheckProcessor的实例只演示了JSR-269嵌入式注解处理API其中的一部分功能,基于这组API支持的项目还有用于效验Hibernate标签使用正确性的Hibernate Validator Annotation Processor(本质上与NameCheckProcessor所做的事情差不多),自动为字段生成getter和setter方法的Project Lombok(根据已有元素生成新的语法树元素)等,

 代码如下:

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.ElementScanner7;
import javax.tools.Diagnostic.Kind;
/**
* javac NameCheckProcessor.java
* javac -processor NameCheckProcessor FT.java
* @author kevin
*
*/


//使用*表示支持所有的Annotations
@SupportedAnnotationTypes(value = "*")
@SupportedSourceVersion(value = SourceVersion.RELEASE_7)
public class NameCheckProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
this.nameCheck = new NameCheck(processingEnv);
}

private NameCheck nameCheck;

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (!roundEnv.processingOver()) {
for (Element element : roundEnv.getRootElements()) {
nameCheck.check(element);
}
}
return false;
}

/**
* 程序名称规范的编译器插件 如果程序命名不合规范,将会输出一个编译器的Warning信息
*
* @author kevin
*
*/
public static class NameCheck {
Messager messager = null;
public NameCheckScanner nameCheckScanner;

private NameCheck(ProcessingEnvironment processingEnv) {
messager = processingEnv.getMessager();
nameCheckScanner = new NameCheckScanner(processingEnv);
}

/**
* 对Java程序明明进行检查,根据《Java语言规范(第3版)》6.8节的要求,Java程序命名应当符合下列格式:
* <ul>
* <li>类或接口:符合驼式命名法,首字母大写。
* <li>方法:符合驼式命名法,首字母小写。
* <li>字段:
* <ul>
* <li>类,实例变量:符合驼式命名法,首字母小写。
* <li>常量:要求全部大写
* </ul>
* </ul>
*
* @param element
*/
public void check(Element element) {
nameCheckScanner.scan(element);
}

/**
* 名称检查器实现类,继承了1.6中新提供的ElementScanner6<br>
* 将会以Visitor模式访问抽象语法数中得元素
*
* @author kevin
*
*/
public static class NameCheckScanner extends ElementScanner7<Void, Void> {
Messager messager = null;

public NameCheckScanner(ProcessingEnvironment processingEnv) {
this.messager = processingEnv.getMessager();
}

/**
* 此方法用于检查Java类
*/
@Override
public Void visitType(TypeElement e, Void p) {
scan(e.getTypeParameters(), p);
checkCamelCase(e, true);
super.visitType(e, p);
return null;
}

/**
* 检查传入的Element是否符合驼式命名法,如果不符合,则输出警告信息
*
* @param e
* @param b
*/
private void checkCamelCase(Element e, boolean initialCaps) {
String name = e.getSimpleName().toString();
boolean previousUpper = false;
boolean conventional = true;
int firstCodePoint = name.codePointAt(0);
if (Character.isUpperCase(firstCodePoint)) {
previousUpper = true;
if (!initialCaps) {
messager.printMessage(Kind.WARNING, "名称:" + name + " 应当已小写字符开头", e);
return;
}
} else if (Character.isLowerCase(firstCodePoint)) {
if (initialCaps) {
messager.printMessage(Kind.WARNING, "名称:" + name + " 应当已大写字母开否", e);
return;
}
} else {
conventional = false;
}
if (conventional) {
int cp = firstCodePoint;
for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) {
cp = name.codePointAt(i);
if (Character.isUpperCase(cp)) {
if (previousUpper) {
conventional = false;
break;
}
previousUpper = true;
} else {
previousUpper = false;
}
}
}
if (!conventional) {
messager.printMessage(Kind.WARNING, "名称:" + name + "应当符合驼式命名法(Camel Case Names)", e);
}
}

/**
* 检查方法命名是否合法
*/
@Override
public Void visitExecutable(ExecutableElement e, Void p) {
if (e.getKind() == ElementKind.METHOD) {
Name name = e.getSimpleName();
if (name.contentEquals(e.getEnclosingElement().getSimpleName())) {
messager.printMessage(Kind.WARNING, "一个普通方法:" + name + " 不应当与类名重复,避免与构造函数产生混淆", e);
checkCamelCase(e, false);
}
}
super.visitExecutable(e, p);
return null;
}

/**
* 检查变量是否合法
*/
@Override
public Void visitVariable(VariableElement e, Void p) {
/* 如果这个Variable是枚举或常量,则按大写命名检查,否则按照驼式命名法规则检查 */
if (e.getKind() == ElementKind.ENUM_CONSTANT || e.getConstantValue() != null || heuristicallyConstant(e)) {
checkAllCaps(e);
} else {
checkCamelCase(e, false);
}
super.visitVariable(e, p);
return null;
}

/**
* 大写命名检查,要求第一个字符必须是大写的英文字母,其余部分可以下划线或大写字母
*
* @param e
*/
private void checkAllCaps(VariableElement e) {
String name = e.getSimpleName().toString();
boolean conventional = true;
int firstCodePoint = name.codePointAt(0);
if (!Character.isUpperCase(firstCodePoint)) {
conventional = false;
} else {
boolean previousUnderscore = false;
int cp = firstCodePoint;
for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) {
cp = name.codePointAt(i);
if (cp == (int) '_') {
if (previousUnderscore) {
conventional = false;
break;
}
previousUnderscore = true;
} else {
previousUnderscore = false;
if (!Character.isUpperCase(cp) && !Character.isDigit(cp)) {
conventional = false;
break;
}
}

}
}
if (!conventional) {
messager.printMessage(Kind.WARNING, "常量:" + name + " 应该全部以大写字母" + "或下划线命名,并且以字符开否", e);
}
}

/**
* 判断一个变量是否是常量
*
* @param e
* @return
*/
private boolean heuristicallyConstant(VariableElement e) {
if (e.getEnclosingElement().getKind() == ElementKind.INTERFACE) {
return true;
} else if (e.getKind() == ElementKind.FIELD && e.getModifiers().containsAll(EnumSet.of(javax.lang.model.element.Modifier.FINAL, javax.lang.model.element.Modifier.STATIC, javax.lang.model.element.Modifier.PUBLIC))) {
return true;
}
return false;
}
}
}

}

在写一个测试类:

import java.util.ArrayList;
import java.util.List;

public class FT {
public static class A_B_C {

}
public static String ab_sd="";
public static int method(List<Integer> list) {
System.out.println("method(List<Integer>list)");
return 1;
}

public static void main(String[] args) {
method(new ArrayList<Integer>());
Long aLong = 2l;
Integer aInteger = 1;
Integer bInteger = 1;
System.out.println(aLong.equals(aInteger + bInteger));
System.out.println(aInteger.equals(1L));
System.out.println("true");
}
}

接下来让我们进入到命令行试试

javac NameCheckProcessor.java 

javac -processor NameCheckProcessor FT.java

FT.java:4: 警告: 名称:FT应当符合驼式命名法(Camel Case Names)

public class FT {

       ^

1 个警告