命名代码检查

根据 中第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

packageannotation.processing;importjava.util.EnumSet;importjava.util.Set;importjavax.annotation.processing.AbstractProcessor;importjavax.annotation.processing.Messager;importjavax.annotation.processing.ProcessingEnvironment;importjavax.annotation.processing.RoundEnvironment;importjavax.annotation.processing.SupportedAnnotationTypes;importjavax.annotation.processing.SupportedSourceVersion;importjavax.lang.model.SourceVersion;importjavax.lang.model.element.Element;importjavax.lang.model.element.ElementKind;importjavax.lang.model.element.ExecutableElement;importjavax.lang.model.element.Name;importjavax.lang.model.element.TypeElement;importjavax.lang.model.element.VariableElement;importjavax.lang.model.util.ElementScanner6;importjavax.tools.Diagnostic.Kind;//使用*表示支持所有的Annotations/*** 以下代码出自 《深入理解Java虚拟机:JVM高级特性与最佳实践》

**/@SupportedAnnotationTypes("*")//表示只对 JDK 1.6 的 Java 源码感兴趣

@SupportedSourceVersion(value =SourceVersion.RELEASE_6)public class NameCheckProcessor extendsAbstractProcessor {

@Overridepublic synchronized voidinit(ProcessingEnvironment processingEnv) {super.init(processingEnv);this.nameCheck = newNameCheck(processingEnv);

}privateNameCheck nameCheck;

@Overridepublic boolean process(Set extends TypeElement>annotations,

RoundEnvironment roundEnv) {if (!roundEnv.processingOver()) {for(Element element : roundEnv.getRootElements()) {

nameCheck.check(element);

}

}return false;

}/*** 程序名称规范的编译器插件 如果程序命名不合规范,将会输出一个编译器的Warning信息

*

*@authorkevin

**/

static classNameCheck {

Messager messager= null;publicNameCheckScanner nameCheckScanner;privateNameCheck(ProcessingEnvironment processingEnv) {

messager=processingEnv.getMessager();

nameCheckScanner= newNameCheckScanner(processingEnv);

}/*** 对Java程序明明进行检查,根据《Java语言规范(第3版)》6.8节的要求,Java程序命名应当符合下列格式:

*

*

类或接口:符合驼式命名法,首字母大写。

*

方法:符合驼式命名法,首字母小写。

*

字段:

*

*

类,实例变量:符合驼式命名法,首字母小写。

*

常量:要求全部大写

*

*

*

*@paramelement*/

public voidcheck(Element element) {

nameCheckScanner.scan(element);

}/*** 名称检查器实现类,继承了1.6中新提供的ElementScanner6

* 将会以Visitor模式访问抽象语法数中得元素

*

**/

static class NameCheckScanner extends ElementScanner6{

Messager messager= null;publicNameCheckScanner(ProcessingEnvironment processingEnv) {this.messager =processingEnv.getMessager();

}/*** 此方法用于检查Java类*/@OverridepublicVoid visitType(TypeElement e, Void p) {

scan(e.getTypeParameters(), p);

checkCamelCase(e,true);super.visitType(e, p);return null;

}/*** 检查传入的Element是否符合驼式命名法,如果不符合,则输出警告信息

*

*@parame

*@paramb*/

private void checkCamelCase(Element e, booleaninitialCaps) {

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);

}

}/*** 检查方法命名是否合法*/@OverridepublicVoid 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;

}/*** 检查变量是否合法*/@OverridepublicVoid 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;

}/*** 大写命名检查,要求第一个字符必须是大写的英文字母,其余部分可以下划线或大写字母

*

*@parame*/

private voidcheckAllCaps(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);

}

}/*** 判断一个变量是否是常量

*

*@parame

*@return

*/

private booleanheuristicallyConstant(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;

}

}

}

}

测试代码:

BADLY_NAMED_CODE

packageannotation.processing;public classBADLY_NAMED_CODE {enumColors {

Red, Blue, Green;

}static final int FORTY_TWO =42;public static int NOT_A_CONSTANT =FORTY_TWO;protected voidBadly_Named_Code() {return;

}public voidNOTcamelCASEmethodNAME() {return;

}

}

执行过程如下:

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 classBADLY_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!

完成了。这是在我的工程里显示的样子:

java编译器代码检查_java 命名代码检查-注解处理器相关推荐

  1. java线程代码实现_Java 多线程代码实现讲解

    作为一个完全面向对象的语言,Java提供了类 java.lang.Thread 来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程.那么如何提供给 Java 我们要线程执行的代码呢? ...

  2. java 同步块原理_Java同步代码块和同步方法原理与应用案例详解

    Java同步代码块和同步方法原理与应用案例详解 发布于 2020-8-7| 复制链接 摘记: 本文实例讲述了Java同步代码块和同步方法.分享给大家供大家参考,具体如下:一 点睛所谓原子性:一段代码要 ...

  3. java for循环效率优化_java 优雅代码for循环 之性能调优

    大家在工作中,用到最多的就是for循环了,但是你知道如何写for循环可以提高系统的性能呢? 我从以下几个方面对如何写for循环给大家几个事例,希望可以帮到你们 原始代码 优化过的代码 对比结果 在循环 ...

  4. java同步方法同步块_java 同步代码块与同步方法

    同步代码块 synchronized(obj) {//代码块 } obj 为同步监视器,以上代码的含义为:线程开始执行同步代码块(中的代码)之前,必须先获得对同步监视器的锁定. 代码块中的代码是执行代 ...

  5. java编译器使用教程_Java编译器API简介

    今天给大家分享的是Java编译器API简介,文章部分内容摘自[优锐课]学习笔记. Java编译器API Java编译器API是Java模块(称为java.compiler)的一部分.该模块包括语言模型 ...

  6. java类静态初始化_Java静态代码块和类初始化、实例初始化过程

    1. 静态代码块 静态代码块:定义在类中方法外,使用static修饰 ①可以为类变量(静态变量)初始化 ②静态代码块在第一次使用这个类之前执行,即在类初始化时执行,且只执行一次 ③若有多个静态代码块, ...

  7. java 实体字段变更记录_java – Hibernate:检查哪个实体的字段被修改

    我拥有的: 我有Hibernate实体,它包含许多非瞬态字段,包​​括集合.用户可以一次更新每个字段或一组字段. 有什么挑战 在处理程序中,我应该检查实体的哪个字段已被更改: public void ...

  8. java同步方法完成案例_Java同步代码块和同步方法原理与应用案例详解

    本文实例讲述了java同步代码块和同步方法.分享给大家供大家参考,具体如下: 一 点睛 所谓原子性WOmoad:一段代码要么执行,要么不执行,不存在执行一部分被中断的情况.言外之意是这段代码就像原子一 ...

  9. java 代码同步_Java同步代码块 转

    Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...

最新文章

  1. Docker4Dev #7 新瓶装老酒 – 使用 Windows Container运行ASP.NET MVC 2 + SQLExpress 应用
  2. 【 Vivado 】Working with Sources in Non-Project Mode
  3. php如何检测在另一台设备登录怎么做_如何使用Metasploit的Web Delivery Script和命令注入弹出Shell(翻译)...
  4. Logica实战与剖析(1)
  5. Codeforces Round #383 _python作死系列
  6. uva-10602-贪心
  7. 打字机已经被计算机所取代用英语,无法被电脑所取代的职业
  8. hutool 自定义excel_Hutool Java 工具类库导出 Excel,超级简单!
  9. vue @click 如何绑定多个方法
  10. python mysql 写入_python如何写进MYSQL
  11. ffmpeg之让视频快进
  12. 【雷达通信】基于matlab雷达数字信号处理【含Matlab源码 281期】
  13. 用java怎么让时间走动起来,java脚本实现时间刷新
  14. 小说关于计算机名称,小说取名和人名取名太纠结了,感觉橙瓜码字的自动取名还不错...
  15. 金蝶KIS商贸高级版V6.1开发实现‘序时簿中心’增加'联系人'字段列
  16. Postgresql动态执行EXECUTING语法解析过程
  17. proe5.0启动失败,光标转了几圈后没有任何反应
  18. 低智商和高智商的9个表现是什么
  19. Sharding-Sphere的应用性能监控实践
  20. 阿里达摩盘:双11大促人群诊断、DEEPLINK洞察、大促标签定制

热门文章

  1. 从零开始搭建spring-cloud(5) ----zuul
  2. 又一个4000字肝货,详解tkinter图形化界面制作流程!
  3. 假设检验在数据分析中的应用
  4. 安利!阿里程序员常用的 15 款开发者工具
  5. mysql数据记录更新版本问题_MySQL版本升级遇到的问题小结
  6. 2020年最好用的手机是哪一款_2020年好评最多的三款手机,看看你在用哪款?
  7. python离散余弦变换_在python3下使用OpenCV做离散余弦变换DCT及其反变换IDCT
  8. python使用循环结构计算10_十二、 python中的循环结构
  9. sqoop数据倾斜_北京卓越讯通大数据岗位面试题分享
  10. java中浮点数的表示_java 浮点数表示法