首页 > 其他分享 >插入式注解处理器

插入式注解处理器

时间:2024-12-01 14:43:41浏览次数:4  
标签:java name javac anno 插入式 import 注解 false 处理器

实战:插入式注解处理器

目标:检查是否符合驼式命名法

详细描述查看【深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)】10.4 实战:插入式注解处理器(510页)

1.代码部分

1.注解处理器

NameCheckProcessor

package org.anno;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Set;

// 可以用"*"表示支持所有Annotations
@SupportedAnnotationTypes("*")
// 只支持JDK 6的Java代码
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class NameCheckProcessor extends AbstractProcessor {
    private NameChecker nameChecker;

    /**
     * 初始化名称检查插件
     */
    @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;

    }

}
2.命名检查器

NameChecker

package org.anno;

import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.*;
import javax.lang.model.util.ElementScanner8;

import java.util.EnumSet;

import static javax.lang.model.element.ElementKind.*;
import static javax.lang.model.element.Modifier.*;
import static javax.tools.Diagnostic.Kind.WARNING;

/**
 * 程序名称规范的编译器插件:<br>
 * 如果程序命名不合规范,将会输出一个编译器的WARNING信息
 */
public class NameChecker {
    private final Messager messager;
    NameCheckScanner nameCheckScanner = new NameCheckScanner();

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

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

    /**
     * 名称检查器实现类,继承了JDK 6中新提供的ElementScanner6<br>
     * 将会以Visitor模式访问抽象语法树中的元素
     */
    private class NameCheckScanner extends ElementScanner8<Void, Void> {
        /**
         * 此方法用于检查Java类
         */
        @Override
        public Void visitType(TypeElement e, Void p) {
            scan(e.getTypeParameters(), p);
            checkCamelCase(e, true);
            super.visitType(e, p);
            return null;
        }

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

        /**
         * 检查变量命名是否合法
         */
        @Override
        public 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);
        }
    }
}
3.不规范命名的代码
package org.anno;

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

2.运行与测试

待编译的类目录:D:\ToolOfProductionData\IDEAFile\DemoTest\src\main\java\org\anno

1.运行测试

注意:因为有包名的缘故,如果直接在包下执行javac或者java 这样是找不到class文件的。

解决:去对应项目的java目录下执行命令 例如:cd D:\ToolOfProductionData\IDEAFile\DemoTest\src\main\java

d:
cd D:\ToolOfProductionData\IDEAFile\DemoTest\src\main\java
javac org/anno/NameChecker.java
有编码问题是则使用:javac -encoding utf-8 org/anno/NameChecker.java
javac org/anno/NameCheckProcessor.java

javac -processor org.anno.NameCheckProcessor org\anno\BADLY_NAMED_CODE.java
有编码问题是则使用:
javac -processor org.anno.NameCheckProcessor -encoding utf-8 org\anno\BADLY_NAMED_CODE.java

运行效果图

注意:javac编译问题

因为有包名的缘故,如果直接在包下执行javac或者java 这样是找不到 class文件的。

解决:去对应项目的java目录下执行命令 例如:D:\ToolOfProductionData\IDEAFile\DemoTest\src\main\java

详细请看:javac编译问题_找不到注释处理程序-CSDN博客

编译条件

待编译的类目录:D:\ToolOfProductionData\IDEAFile\DemoTest\src\main\java\org\anno

1.中文乱码

javac NameChecker.java

改成:

javac -encoding utf-8 NameChecker.java

2.引用其他类的时候,报找不到符号

(1)javac -encoding utf-8 *.java

(2) 换到src\main\java目录下:cd C:\work\workspace\my\my-utils\src\main\java

javac -encoding utf-8 com/my/jvm/processor/NameCheckProcessor.java

3.javac -processor报错

(1)java.lang.NoClassDefFoundError: NameCheckProcessor (wrong name: com/my/jvm/processor/NameCheckProcessor)

javac -processor NameCheckProcessor -encoding utf-8 .\BADLY_NAMED_CODE.java

(2)错误: 找不到注释处理程序 'com.my.jvm.processor.NameCheckProcessor'

javac -processor com.my.jvm.processor.NameCheckProcessor -encoding utf-8 .\BADLY_NAMED_CODE.java

因为有包名的缘故,如果直接在包下执行javac或者java 这样是找不到 class文件的。

解决:

cd  C:\work\workspace\my\my-utils\src\main\java

javac  -processor com.my.jvm.processor.NameCheckProcessor  -encoding utf-8  com\my\jvm\processor\BADLY_NAMED_CODE.java

标签:java,name,javac,anno,插入式,import,注解,false,处理器
From: https://www.cnblogs.com/hellodeyang/p/18565997

相关文章

  • 从源码角度深入剖析Spring Bean对象创建过程中各后置处理器的作用与实现原理
            springioc容器刷新的过程中涵盖了bean对象的创建流程,再bean对象创建的过程中,使用了哪些后置处理器(扩展接口)?这些后置处理器又有什么作用?下图为spring容器刷新过程中,各处理器的位置。        本文着重对BeanDefinitionRegistryPostProcessor、MergedB......
  • spring的注解开发-事务@transactional的使用
    新建maven项目pom<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http......
  • 从 @Import 注解到自动配置
    推荐阅读:Spring@Import注解用法、SpringBootStarter是什么@Import注解用于把实例加入SpringIOC容器中。打开@SpringBootApplication注解(SpringBoot2.6.13为例),会发现其被@EnableAutoConfiguration标注:@Target(ElementType.TYPE)@Retention(RetentionPolicy.R......
  • RUOYI参数验证异常处理及自定义注解触发验证抛出异常报错
    目录一.ruoyi与参数验证1.触发报错2.后端现象二.源码分析1.前端代码2.后端代码3.报错分析三.自定义函数注解1.NoNumber注解2.NoNumberMain校验器3.将注解添加进SysRole中4.前端&后端现象一.ruoyi与参数验证1.触发报错对参数验证的使用,从触发参数报错开始,首先对ru......
  • @Configuration 和 @AutoConfiguration 注解
    参考:https://stackoverflow.com/a/74117678@Configuration是一个Spring注解,并不严格绑定于SpringBoot。它的作用是支持以编程方式创建SpringBean,以简化XML配置。@AutoConfiguration是SpringBoot提供的注解。它的存在是为了让与SpringBoot协作的外部服务提供者能......
  • 第13章 全注解开发
    第十三章全注解开发13.1web.xml文件的替代13.1.1Servlet3.0新特性Servlet3.0新特性:web.xml文件可以不写了。在Servlet3.0的时候,规范中提供了一个接口:服务器在启动的时候会自动从容器中找ServletContainerInitializer接口的实现类,自动调用它的onStartup方法来完成......
  • Spring 中的 @Value 注解
    Spring中的@Value注解引言:Spring与依赖注入在Java开发中,Spring框架是一个不可或缺的工具,它通过依赖注入(DI)和面向切面编程(AOP)等技术,简化了企业级应用的开发。依赖注入是Spring的核心概念之一,它允许开发者将对象的依赖关系交由Spring容器管理,而不是在代码中硬编......
  • MyBatis与Spring整合中@Param注解的作用与使用详解
    在使用MyBatis进行参数绑定时,@Param注解扮演了重要的角色,特别是在涉及多参数传递或参数名不一致的场景。本文将详细解析@Param的作用、原理以及最佳实践。我的实体类一、什么是@Param注解?@Param是MyBatis提供的一个注解,用于为方法参数指定别名,以便在SQL映射文件......
  • spring的注解开发-注解方式整合Mybaits
    新建maven项目pom<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="htt......
  • SpringMVC框架---SpringMVC概述、入门案例、常用注解
    目录第一章:三层架构和MVC1.三层架构2.MVC模型第二章:SpringMVC的入门案例1.SpringMVC的简介1.1SpringMVC介绍1.2SpringMVC执行过程2.SpringMVC的入门程序创建WEB工程,引入开发的jar包项目代码3.入门案例的执行过程分析4.RequestMapping注解第三章:请求参数的......