做图的ppt模板下载网站,网站内容如何更新,wordpress取消伪静态,牛牛网站建设JDK 的编译子系统暴露给用户直接控制的功能相对很少#xff0c;除了虚拟机即时编译的若干参数#xff0c;便只有 JSR-296 中定义的插入式注解处理器 API#xff1b; 文章目录1. 目标2. 实现3. 运行与测试4. 其他应用案例1. 目标
前端编译器在讲 Java 源码编译成字节码时对源…JDK 的编译子系统暴露给用户直接控制的功能相对很少除了虚拟机即时编译的若干参数便只有 JSR-296 中定义的插入式注解处理器 API 文章目录1. 目标2. 实现3. 运行与测试4. 其他应用案例1. 目标
前端编译器在讲 Java 源码编译成字节码时对源码做了各方面检查主要是看程序写到对不对较少去看写得好不好业界有一些如 CheckStyle、FindBug、Klocwork 等的工具用于检查代码好坏有一些用于扫描 Java 源码也有一些是扫描字节码我们的目标是通过注解处理器 API 实现自己编码风格的校验工具NameCheckProcessor
根据《Java 语言规范》要求Java 程序命名推荐使用如下书写规范
类接口驼峰命名法首字母大写方法驼峰命名法首字母小写字段 类或实例变量驼峰命名法首字母小写常量全部由大写字母或下划线构成第一个字符不能是下划线
为 javac 编译器添加额外的功能在编译程序时检查程序名是否符合上述对类、接口、方法、字段的命名要求
2. 实现
注解处理器可以通过继承抽象类 javax.annotation.processing.AbstractProcessor 复写抽象方法 process() 来实现javac 编译器在执行注解处理器代码时会调用 process()
annotations此注解处理器所要处理的注解集合roundEnv当前这个轮次Round中的抽象语法树节点每个语法树节点表示一个 ElementprocessingEnvAbstractProcessor 中的一个 protected 变量代表了注解处理器框架提供的一个上下文环境可用来创建新的代码、向编译器输出信息、获取其他工具类等
javax.lang.model.ElementKind 定义了 18 类 Element包含 Java 代码中可能出现的全部元素如包Package、枚举Enum、类Class、注解AnnotationType、接口Interface、枚举值EnumConstant、字段Field、参数Parameter、本地变量LocalVariable、异常ExceptionnParameter、方法Method、构造函数Constructor、静态语句块StaticInit即 static{} 块、实例语句块InstanceInit即 {} 块、参数化类型TypeParameter泛型尖括号内的类型、资源变量ResourceVariabletry-resource 中定义的变量、模块Module、未定义的其他语法树节点Other
SupportedAnnotationTypes代表这个注解处理器对哪些注解感兴趣SuppertedSourceVersion表示这个注解处理器可以处理哪些版本的 Java 代码
注解处理器 NameCheckProcessor /*** * 表示支持所有 Annotations* 只支持 JDK 8 的 Java 代码** author Aurelius Shu* since 2023-02-19*/
SupportedAnnotationTypes(*)
SupportedSourceVersion(SourceVersion.RELEASE_8)
public class NameCheckProcessor extends AbstractProcessor {private NameChecker nameChecker;/*** 初始化名称检查插件*/Overridepublic void init(ProcessingEnvironment processingEnv) {super.init(processingEnv);nameChecker new NameChecker(processingEnv);}/*** 对输入的语法树的各个节点进行名称检查*/Overridepublic boolean process(Set? extends TypeElement annotations, RoundEnvironment roundEnv) {if (!roundEnv.processingOver()) {for (Element element : roundEnv.getRootElements())nameChecker.checkNames(element);}return false;}
}命名检查器 NameChecker /*** 程序名称规范的编译器插件* 如果程序命名不合规范将会输出一个编译器的 WARNING 信息** author Aurelius Shu* since 2023-02-19*/
public class NameChecker {private final Messager messager;NameCheckScanner nameCheckScanner new NameCheckScanner();NameChecker(ProcessingEnvironment processsingEnv) {this.messager processsingEnv.getMessager();}/*** 对Java程序命名进行检查根据《Java语言规范》第三版第 6.8 节的要求Java程序命名应当符合下列格式: * 类或接口:符合驼式命名法首字母大写。* 方法:符合驼式命名法首字母小写。* 字段:* 类、实例变量: 符合驼式命名法首字母小写。* 常量: 要求全部大写。*/public void checkNames(Element element) {nameCheckScanner.scan(element);}/*** 名称检查器实现类继承了 JDK 8 中新提供的ElementScanner8br * 将会以 Visitor 模式访问抽象语法树中的元素*/private class NameCheckScanner extends ElementScanner8Void, Void {/*** 此方法用于检查 Java 类*/Overridepublic Void visitType(TypeElement e, Void p) {scan(e.getTypeParameters(), p);checkCamelCase(e, true);super.visitType(e, p);return null;}/*** 检查方法命名是否合法*/Overridepublic Void visitExecutable(ExecutableElement e, Void p) {if (e.getKind() METHOD) {Name name e.getSimpleName();if (name.contentEquals(e.getEnclosingElement().getSimpleName())) {messager.printMessage(WARNING, 一个普通方法 “ name ”不应当与类名重复避免与构造函数产生混淆, e);}checkCamelCase(e, false);}super.visitExecutable(e, p);return null;}/*** 检查变量命名是否合法*/Overridepublic Void visitVariable(VariableElement e, Void p) {// 如果这个Variable是枚举或常量则按大写命名检查否则按照驼式命名法规则检查if (e.getKind() ENUM_CONSTANT || e.getConstantValue() ! null || heuristicallyConstant(e))checkAllCaps(e);else checkCamelCase(e, false);return null;}/*** 判断一个变量是否是常量*/private boolean heuristicallyConstant(VariableElement e) {if (e.getEnclosingElement().getKind() INTERFACE) return true;else if (e.getKind() FIELD e.getModifiers().containsAll(EnumSet.of(PUBLIC, STATIC, FINAL)))return true;else {return false;}}/*** 检查传入的 Element 是否符合驼式命名法如果不符合则输出警告信息*/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(WARNING, 名称“ name ”应当以小写字母开头, e);return;}} else if (Character.isLowerCase(firstCodePoint)) {if (initialCaps) {messager.printMessage(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(WARNING, 名称“ name ”应当符合驼式命名法(Camel Case Names), e);}/*** 大写命名检查要求第一个字母必须是大写的英文字母其余部分可以是下划线或大写字母*/private void checkAllCaps(Element 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(WARNING, 常量“ name ”应当全部以大写字母或下划线命名并且以字母开头, e);}}}
}NameChecker 通过继承 javax.lang.model.util.ElementScanner8 的 NameCheckScanner 类以 Visitor 模式实现对语法树的遍历通过 checkCamelCase() 与 checkAllCaps() 实现驼峰命名法和全大写命名的检查分别在 visitType()、visitVariable()、visitExecutable() 中对类、字段、方法进行检查
3. 运行与测试
不符合规范的代码演示
public class BADLY_NAMED_CODE {enum colors {red, blue, green;}static final int _FORTY_TWO 42;public static int NOT_A_CONSTANT _FORTY_TWO;protected void BADLY_NAMED_CODE() {return;}public void NOTcamelCASEmethodNAME() {return;}
}运行注解处理器
# 编译注解处理器切换到 src/main/java 路径
javac edu/aurelius/jvm/compiler/NameChecker.java
javac edu/aurelius/jvm/compiler/NameCheckProcessor.java# 编译时运行注解处理器
javac -processor edu.aurelius.jvm.compiler.NameCheckProcessor edu/aurelius/jvm/compiler/BADLY_NAMED_CODE.java检查效果
edu/aurelius/jvm/compiler/BADLY_NAMED_CODE.java:7: warning: 名称“BADLY_NAMED_CODE”应当符合驼式命名法(Camel Case Names)
public class BADLY_NAMED_CODE {^
edu/aurelius/jvm/compiler/BADLY_NAMED_CODE.java:8: warning: 名称“colors”应当以大写字母开头enum colors {^
edu/aurelius/jvm/compiler/BADLY_NAMED_CODE.java:9: warning: 常量“red”应当全部以大写字母或下划线命名并且以字母开头red, blue, green;^
edu/aurelius/jvm/compiler/BADLY_NAMED_CODE.java:9: warning: 常量“blue”应当全部以大写字母或下划线命名并且以字母开头red, blue, green;^
edu/aurelius/jvm/compiler/BADLY_NAMED_CODE.java:9: warning: 常量“green”应当全部以大写字母或下划线命名并且以字母开头red, blue, green;^
edu/aurelius/jvm/compiler/BADLY_NAMED_CODE.java:12: warning: 常量“_FORTY_TWO”应当全部以大写字母或下划线命名并且以字母开头static final int _FORTY_TWO 42;^
edu/aurelius/jvm/compiler/BADLY_NAMED_CODE.java:13: warning: 名称“NOT_A_CONSTANT”应当以小写字母开头public static int NOT_A_CONSTANT _FORTY_TWO;^
edu/aurelius/jvm/compiler/BADLY_NAMED_CODE.java:15: warning: 一个普通方法 “BADLY_NAMED_CODE”不应当与类名重复避免与构造函数产生混淆protected void BADLY_NAMED_CODE() {^
edu/aurelius/jvm/compiler/BADLY_NAMED_CODE.java:15: warning: 名称“BADLY_NAMED_CODE”应当以小写字母开头protected void BADLY_NAMED_CODE() {^
edu/aurelius/jvm/compiler/BADLY_NAMED_CODE.java:19: warning: 名称“NOTcamelCASEmethodNAME”应当以小写字母开头public void NOTcamelCASEmethodNAME() {^
10 warnings-XprintRounds、-XprintProcessorInfo 参数可以进一步跟踪注解处理器的运作详细信息
4. 其他应用案例
校验 Hibernate 标签的正确性Hibernate Validator Annotation Processor自动为字段生成 getter、setter 方法等辅助内容Lombok根据已有元素生成新的语法树元素 上一篇「JVM 编译优化」Java 语法糖泛型、自动装箱/拆箱、条件编译
PS感谢每一位志同道合者的阅读欢迎关注、评论、赞 参考资料
[1]《深入理解 Java 虚拟机》