深入JAVA虚拟机学习笔记(第十章)-使用Java注解处理器编写一个建议命名检查插件

 2019-12-22 11:08  阅读(1031)
文章分类:JVM

代码

注意:每一个注解处理器都是单例!

NameCheckProcessor

package ztf;

    import java.util.Set;

    import javax.annotation.processing.AbstractProcessor;
    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.TypeElement;

    //可以用"*"表示支持所有Annotations,可以使用通配符,如果需要不同包下的话可以使用{}进行分割
    @SupportedAnnotationTypes("*")
    //这里填写支持的java版本
    @SupportedSourceVersion(SourceVersion.RELEASE_8)
    public class NameCheckProcessor extends AbstractProcessor {

         private NameChecker nameChecker;

         /** * 初始化名称检查插件,processingEnv为注解处理器提供的上下文环境 */
         @Override
         public void init(ProcessingEnvironment processingEnv) {
             super.init(processingEnv);
             nameChecker = new NameChecker(processingEnv);
         }

         /** * 对输入的语法树的各个节点进行进行名称检查 */
         @Override
         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
             if (!roundEnv.processingOver()) {
                 for (Element element : roundEnv.getRootElements())
                     //这里可以执行想要的操作
                     nameChecker.checkNames(element);
             }
             return false;
         }

    }

NameChecker

package ztf;

    import java.util.EnumSet;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

    import javax.annotation.processing.Messager;
    import javax.annotation.processing.ProcessingEnvironment;
    import javax.lang.model.element.Element;
    import javax.lang.model.element.ElementKind;
    import javax.lang.model.element.ExecutableElement;
    import javax.lang.model.element.Modifier;
    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.lang.model.util.ElementScanner8;
    import javax.tools.Diagnostic.Kind;

    //为了演示效果,命名正常的类也会以警告级别显示出来
    public class NameChecker {
        //Messager用于向编译器发送信息
        private final Messager messager;

        NameCheckScanner nameCheckScanner = new NameCheckScanner();

        NameChecker(ProcessingEnvironment processsingEnv) {
            this.messager = processsingEnv.getMessager();
        }

        public void checkNames(Element element) {
            nameCheckScanner.scan(element);
        }

        /** * 名称检查器实现类 * 将会以Visitor模式访问抽象语法树中的元素 * 命名规则判断中将不对语法树进行修改,因此全部返回值都为null */
        private class NameCheckScanner extends ElementScanner8<Void, Void> {

            /** * 此方法用于检查类名 * 带可变参数Void,该参数包含9种类型 */
            @Override
            public Void visitType(TypeElement e, Void p) {
                scan(e.getTypeParameters(), p);
                checkClassName(e);
                super.visitType(e, p);
                return null;
            }

            //首字母大写
            public void checkClassName(TypeElement e) {
                  String name = e.getSimpleName().toString();
                  if("".equals(name)||name==null) {
                      messager.printMessage(Kind.WARNING, "类名" + name + "出现异常", e);
                  }
                String regEx ="[A-Z][A-Za-z0-9]{0,}";
                Pattern pattern = Pattern.compile(regEx);
                Matcher matcher = pattern.matcher(name);
                if(matcher.matches()) {
                    messager.printMessage(Kind.WARNING, "类名" + name + "符合驼式命名法,首字母大写", e);
                }else {
                    messager.printMessage(Kind.WARNING, "类名" + name + "不符合符合驼式命名法", e);
                }
            }

            /** * 检查方法命名是否合法 */
            @Override
            public Void visitExecutable(ExecutableElement e, Void p) {
                if (e.getKind() == ElementKind.METHOD) {
                    Name name = e.getSimpleName();
                    checkMethodName(e);
                }
                super.visitExecutable(e, p);
                return null;
            }

            //首字母大写
            public void checkMethodName(ExecutableElement e) {
                String name = e.getSimpleName().toString();
                if("".equals(name)||name==null) {
                      messager.printMessage(Kind.WARNING, "方法名" + name + "出现异常", e);
                }
                String regEx ="[a-z][A-Za-z0-9]{0,}";
                Pattern pattern = Pattern.compile(regEx);
                Matcher matcher = pattern.matcher(name);
                if(matcher.matches()) {
                    messager.printMessage(Kind.WARNING, "方法名" + name + "符合驼式命名法,首字母小写", e);
                }else {
                    messager.printMessage(Kind.WARNING, "方法名" + name + "不符合符合驼式命名法,首字母小写", e);
                }
            }

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

            public void checkField(VariableElement e) {
                String name = e.getSimpleName().toString();
                if("".equals(name)||name==null) {
                      messager.printMessage(Kind.WARNING, "字段名" + name + "出现异常", e);
                }
                String regEx ="[a-z][A-Za-z0-9]{0,}";
                Pattern pattern = Pattern.compile(regEx);
                Matcher matcher = pattern.matcher(name);
                if(matcher.matches()) {
                    messager.printMessage(Kind.WARNING, "字段名" + name + "符合驼式命名法,首字母小写", e);
                }else {
                    messager.printMessage(Kind.WARNING, "字段名" + name + "不符合符合驼式命名法,首字母小写", e);
                }
            }
            public void checkEnumFinal(VariableElement e) {
                String name = e.getSimpleName().toString();
                if("".equals(name)||name==null) {
                      messager.printMessage(Kind.WARNING, "常量" + name + "出现异常", e);
                }
                String regEx ="[A-Z][A-Z_]{0,}";
                Pattern pattern = Pattern.compile(regEx);
                Matcher matcher = pattern.matcher(name);
                if(matcher.matches()) {
                    messager.printMessage(Kind.WARNING, "常量" + name + "符合要求全部大写字母或下划线构成,并且第一个字符不能是下划\r\n" + 
                            "线", e);
                }else {
                    messager.printMessage(Kind.WARNING, "常量" + name + "不符合要求全部大写字母或下划线构成,并且第一个字符不能是下划\r\n" + 
                            "线", e);
                }
            }
            /** * 判断一个变量是否是常量 */
            private boolean heuristicallyConstant(VariableElement e) {
                //获得封闭该变量的类,看是否为借口
                if (e.getEnclosingElement().getKind() == ElementKind.INTERFACE)
                    return true;
                else if (e.getKind() == ElementKind.FIELD && e.getModifiers().containsAll(EnumSet.of(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)))
                    return true;
                else {
                    return false;
                }
            }

        }
    }

运行与测试

这里可以通过javac的-processor来让编译时附上自己编写的自定义注解处理器。

在Eclipse可以使用另外一种方法:

在src或者工程目录下创建文件夹META-INF/services,然后在services下创建文件javax.annotation.processing.Processor
里面写上注解处理器的全类名(包括包名)

如:
ztf.NameCheckProcessor

然后到处为jar文件,在你需要用到这个注解处理器的工程里右键Proerties没,大功告成!:
2019120001591\_1.png
2019120001591\_2.png

点赞(1)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> 深入JAVA虚拟机学习笔记(第十章)-使用Java注解处理器编写一个建议命名检查插件

相关推荐