当前位置: 首页 > news >正文

「JVM 编译优化」插入式注解处理器(自定义代码编译检查)

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)中的抽象语法树节点,每个语法树节点表示一个 Element;
  • processingEnv,AbstractProcessor 中的一个 protected 变量,代表了注解处理器框架提供的一个上下文环境,可用来创建新的代码、向编译器输出信息、获取其他工具类等;

javax.lang.model.ElementKind 定义了 18 类 Element,包含 Java 代码中可能出现的全部元素,如包(Package)、枚举(Enum)、类(Class)、注解(AnnotationType)、接口(Interface)、枚举值(EnumConstant)、字段(Field)、参数(Parameter)、本地变量(LocalVariable)、异常(ExceptionnParameter)、方法(Method)、构造函数(Constructor)、静态语句块(StaticInit,即 static{} 块)、实例语句块(InstanceInit,即 {} 块)、参数化类型(TypeParameter,泛型尖括号内的类型)、资源变量(ResourceVariable,try-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 中新提供的ElementScanner8<br> * 将会以 Visitor 模式访问抽象语法树中的元素*/private class NameCheckScanner extends ElementScanner8<Void, 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.ElementScanner8NameCheckScanner 类,以 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 虚拟机》

相关文章:

「JVM 编译优化」插入式注解处理器(自定义代码编译检查)

JDK 的编译子系统暴露给用户直接控制的功能相对很少&#xff0c;除了虚拟机即时编译的若干参数&#xff0c;便只有 JSR-296 中定义的插入式注解处理器 API&#xff1b; 文章目录1. 目标2. 实现3. 运行与测试4. 其他应用案例1. 目标 前端编译器在讲 Java 源码编译成字节码时对源…...

一文彻底理解大小端和位域 BIGENDIAN LITTLEENDIAN

一文彻底理解大小端和位域 为什么有大小端 人们一直认为大道至简&#xff0c;就好像物理学上的世界追求使用一个理论来统一所有的现象。为什么cpu存在大小端之分&#xff0c;一言以蔽之&#xff0c;这两种模式各有各的优点&#xff0c;其各自的优点就是对方的缺点&#xff0c…...

面试准备知识点与总结——(虚拟机篇)

目录JVM的内存结构JVM哪些部分会发生内存溢出方法区、永久代、元空间三者之间的关系JVM内存参数JVM垃圾回收算法1.标记清除法2.标记整理3.标记复制说说GC和分代回收算法三色标记与并发漏标的问题垃圾回收器项目中什么时候会内存溢出&#xff0c;怎么解决类加载过程三个阶段何为…...

spring cloud 集成 seata 分布式事务

spring cloud 集成 seata 分布式事务 基于 seata-server 1.6.x 序言 下载 seata-server 准备一个数据库 seata 专门为 seata-server 做存储&#xff0c;如, 可以指定 branch_tabledistributed_lockglobal_tablelock_table 准备一个业务库&#xff0c;比如存放定单&#xff…...

k8s篇之概念介绍

文章目录时光回溯什么是K8SK8S不是什么一、K8S构成组件控制平面组件&#xff08;Control Plane Components&#xff09;kube-apiserveretcdkube-schedulerkube-controller-managercloud-controller-managerNode 组件kubeletkube-proxy容器运行时&#xff08;Container Runtime&…...

JavaScript学习第1天:浏览器组成、JS的组成、变量、数据类型转化、运算符、while和do...while循环

目录一、浏览器的组成二、JS的组成三、变量1、同时声明多个变量2、声明变量特殊情况四、数据类型1、数据类型2、 isNaN()方法3、字符串转义符4、字符串拼接5、特殊拼接五、数据类型转换1、转化为字符串2、转化为数字型3、转化为布尔值六、运算符1、递增和递减运算符2、逻辑运算…...

【Flutter入门到进阶】Dart进阶篇---Dart多线程异步原理

1 Isolate 1.1 什么是Isolate 1.1.1 概念 线程&#xff1f;异步&#xff1f;隔离&#xff1f;到底什么意思&#xff1f; Isolate中文意思是隔离&#xff0c;从使用角度来说是Dart的线程&#xff0c;但是从本质虚拟机的实现角度来讲Isolate是一组封装。 isolate可以理解为dar…...

WEB系列(二)-----------XSS

XSS原理及基础 定义 恶意攻击者会往Web页面里插入JS代码,当用户点击网页时.镶嵌的JS代码就会执行,从而达到恶意的特殊目的. 原因 程序对输入和输出的控制不够严格&#xff0c;导致payload输出到前段时被浏览器当做有效代码执行从而产生危害。 分类 存储型反射型DOM型 测…...

[python入门㊾] - python异常中的断言

目录 ❤ 断言的功能与语法 ❤ 常用断言 ❤ 常用的断言表达方式 ❤ 异常断言 ❤ 正则断言 ❤ 检查断言装饰器 ❤ 断言的功能与语法 Python assert&#xff08;断言&#xff09;用于判断一个表达式&#xff0c;在表达式条件为 False 的时候触发异常 断言可以在条件…...

一文告诉你什么是财务数据治理?

大家好&#xff0c;我是梦想家Alex&#xff0c;今天是周末&#xff0c;就不给大家分享技术文了&#xff5e;应出版社老师推荐&#xff0c;文末给大家送几本DAMA中国主席力荐&#xff0c;20位行业专家历时2年共同打造的《财务数据治理实战》&#xff0c;将数据治理理论应用于财务…...

MySQL数据库调优————ORDER BY语句

ORDER BY调优的核心原理&#xff0c;原则是利用索引的有序性跳过排序环节 关于ORDER BY语句的一些尝试 我们使用employees表进行尝试&#xff0c;索引情况如下 在执行计划的结果中&#xff0c;Extra里如果存在&#xff0c;Using filesort则表示&#xff0c;排序没有使用到索…...

Linux命令之grep

Linux grep是一个非常强大的文本搜索工具。按照给定的正则表达式对目标文本进行匹配检查&#xff0c;打印匹配到的行。grep命令可以跟其他命令一起使用&#xff0c;对其他命令的输出进行匹配。 grep语法如下&#xff1a; grep [options] [pattern] content 文本检索 grep可以对…...

一起学 pixijs(4):如何绘制文字md

大家好&#xff0c;我是前端西瓜哥&#xff0c;今天我们来学 pixijs 如何绘制文字。pixijs 版本为 7.1.2。 使用原生的 WebGL 来绘制文字是非常繁琐的&#xff0c;pixijs 对此进行了高层级的封装&#xff0c;提供了 Text 类和 BitMapText 类来绘制文字。 Text 最基本的写法&…...

mac m1设备上安装Qt并使用qt编程遇到的问题以及解决方式

# 简介&#xff1a; 首先在M1平台上的程序可以看到有两种架构&#xff0c;分别是intel的&#xff08;x86-64&#xff09;和苹果的m1&#xff08;arm64架构&#xff09;&#xff0c;根据苹果的介绍&#xff0c;当在m1上面运行intel程序的时候使用的是转译的方式运行的&#xff…...

tensorflow 学习笔记(二):神经网络的优化过程

前言&#xff1a; 学习跟随 如何原谅奋力过但无声的 tensorflow 笔记笔记。 本章主要讲解神经网络的优化过程&#xff1a;神经网络的优化方法&#xff0c;掌握学习率、激活函数、损失函数和正则化的使用&#xff0c;用 Python 语言写出 SGD、Momentum、Adagrad、RMSProp、Ada…...

【Java】《Java8 实战》 CompletableFuture 学习

文章目录前言1. 并发(Concurrent) 和 并行(Parallel)1.1 并发的来源1.2 并发技术解决了什么问题2. 并行的来源2.1 并行解决了什么问题3. CompletableFuture 简介4. CompletableFuture 简单应用5. CompletableFuture 工厂方法的应用6. CompletableFuture join() 方法7. 使用 Par…...

Vue3之条件渲染

1.何为条件渲染 条件渲染就是在指定的条件下&#xff0c;渲染出指定的UI。比如当我们显示主页的时候&#xff0c;应该隐藏掉登录等一系列不相干的UI元素。即UI元素只在特定条件下进行显示。而在VUE3中&#xff0c;这种UI元素的显示和隐藏可以通过两个关键字&#xff0c;v-if 和…...

将Nginx 核心知识点扒了个底朝天(四)

为什么 Nginx 不使用多线程&#xff1f; Apache: 创建多个进程或线程&#xff0c;而每个进程或线程都会为其分配 cpu 和内存&#xff08;线程要比进程小的多&#xff0c;所以 worker 支持比 perfork 高的并发&#xff09;&#xff0c;并发过大会榨干服务器资源。 Nginx: 采用…...

设计模式之工厂模式

文章の目录一、什么是工厂模式二、工厂模式有什么用&#xff1f;三、应用场景四、示例1、用字面量的方式创建对象2、使用工厂模式创建对象参考写在最后一、什么是工厂模式 工厂模式是一种众所周知的设计模式&#xff0c;广泛应用于软件工程领域&#xff0c;用于抽象创建特定对…...

80.链表-由来

链表是怎么发展来的 线性表&#xff1a;是n个具有相同特性的数据元素的有限序列。 链表&#xff1a;具有线性存储结构的线性表。 为什么需要使用链表&#xff1f;&#xff08;链表是如何被设计出来的&#xff09; 程序开发最重要的部分是如何在项目程序中找到一种合适的、好…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

在Ubuntu24上采用Wine打开SourceInsight

1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

多模态图像修复系统:基于深度学习的图片修复实现

多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...