OpenRewrite框架原理解析
目录
1. OpenRewrite处理流程概述
2. OpenRewrite访问者模式的应用
2.1 访问者模式简介
2.2 OpenRewrite框架如何应用访问者模式
2.2.1 抽象访问者&具体访问者
2.2.2 抽象元素&具体元素
3. LST无损语义树构造
4. 配方(Recipe)执行流程
4.1 执行入口
4.2 LargeSourceSet说明
4.3 配方执行时序图
4.4 配方执行结果表示
OpenRewrite通过将不同类型的源文件构建为Lossless Semantic Trees (LST)无损语义树的数据结构,能够准确和全面地表示源文件的元数据和语义信息。构造完LST后,通过应用访问者模式,将LST数据结构本身与访问LST元素的操作解耦,使得我们能够灵活的自定义各种访问操作,而又不改变LST数据结构。
本文主要对OpenRewrite框架设计和原理进行解析,分析OpenRewrite框架中是如何运用访问者模式进行架构设计的,然后进一步分析访问逻辑的具体执行过程,以便更清楚的掌握OpenRewrite内部执行机理,拨开云雾见月明,进而更好的指导OpenRewrite开发实践。
关于OpenRewrite的介绍和Recipe简单开发实践请参考前述文章:
大规模自动化重构框架--OpenRewrite浅析
OpenRewrite:实现一个简单的配方(Recipe)
1. OpenRewrite处理流程概述
OpenRewrite作为自动化重构的框架,其内部处理流程是通过配方(Recipe)来触发执行的,顶层处理流程如下:
- Recipe:允许使用者自定义重构逻辑的封装类,内部通过getVisitor方法返回构造好的访问器,进而执行访问器的重构规则
- Tree:作为LST(无损语义树)的顶层抽象元素,是所有文件类型中元素的顶层接口类
- SourceFile:所有不同类型文件解析后顶层具体元素的父接口,比如Java源文件解析为J.CompilationUnit是SourceFile的具体实现子类
- LargeSourceSet:需要改写的源文件集合的封装类,内部可以包含不同文件类型的源文件
- TreeVisitor:顶层访问器接口,针对不同的文件类型,派生了不同的子类访问器接口,比如针对Java语言,定义了JavaVisitor
- Changeset/Result:源文件集合(LargeSourceSet)经过配方(Recipe)中的访问器(TreeVisitor)访问后的结果集,包含了重构前(before)和重构后(after)的表示
2. OpenRewrite访问者模式的应用
2.1 访问者模式简介
在具体说明OpenRewrite框架中访问者模式是如何应用的之前,先简单回顾下访问者模式的基本要素:
访问者(Visitor)模式:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。
访问者模式包含的核心类包括:
- 抽象访问者(Visitor):定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
- 具体访问者(ConcreteVisitor):实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
- 抽象元素(Element):声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
- 具体元素(ConcreteElement):实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作
2.2 OpenRewrite框架如何应用访问者模式
在介绍完访问者模式的基本概念后,下面说明下访问者模式在OpenRewrite框架中是如何运用的。
2.2.1 抽象访问者&具体访问者
在OpenRewrite中,抽象访问者为抽象基类TreeVisitor,并且针对不同类型的访问对象,定义了不同类型的访问器,各访问器的整体类图继承结构如下:
- JavaVisitor:针对Java源代码文件的访问器,支持访问Java源代码中的各种元素,比如:包名(Package)、类声明(ClassDeclaration)、方法声明(MethodDeclaration)、变量声明(VariableDeclarations)等
- PropertiesVisitor:针对Properties属性文件的访问器,支持访问属性文件中的键值对、注释等元素
- YamlVisitor:和PropertiesVisitor访问器类似,支持对yaml类型文件的访问
- XmlVisitor:针对Xml类型文件的访问器,支持访问Xml文件的各种元素,包括Tag、属性Attribute等
- MavenVisitor:针对Maven Pom文件的访问器,由于Pom文件也属于Xml格式文件,所以这里MavenVisitor继承了XmlVisitor
- JsonVisitor:针对Json格式文件的访问,比如元素JsonKey、JsonValue等
除此之外,OpenRewrite还提供了针对上述不同类型访问器的XXXIsoVisitor版本的访问器,区别之处是Iso版本的各个visit方法返回的是被访问元素本身,如果访问逻辑不改变被访问元素类型,使用Iso版本可以规避手动类型转换的工作,对使用者更友好。
在如上各种类型的访问器中:
- 通过isAcceptable方法判断该访问器是否可以应用到被访问元素上
- 通过各种visit方法实现不同类型元素的访问
2.2.2 抽象元素&具体元素
在OpenRewrite框架中,顶层抽象元素是接口Tree,并且针对不同的文件类型,扩展了不同的顶层抽象元素,其整体类图如下:
- J:Java项目的顶层抽象元素接口,其子类包括J.CompilationUnit、J.ClassDeclaration、J.Package等具体Java元素
- Properties:Properties属性文件中元素的顶层抽象元素,子类包括具体元素:键值对(Properties.Entry)等
- Xml:Xml文件中元素的顶层抽象元素,子类包含了具体元素:声明(XmlDecl)、Tag等
- Yaml:yaml文件中元素的顶层抽象元素,子类包括:Document、Entry等
- Json:Json格式文件中元素的顶层抽象元素,子类包括具体元素:Member、JsonObject、Array等
在顶层元素Tree中,定义了抽象方法isAcceptable和accept,并交由子类具体实现:
- isAcceptable:用于判断是否可以接受访问器参数的访问
- accept:接受访问器参数的访问,执行访问器中的具体访问逻辑
针对Java语言,这里图示下各种具体元素的类图,如下:
3. LST无损语义树构造
OpenRewrite自动化重构主要涉及2个流程:
1)将源文件解析为LST的过程,根据不同的源文件类型,调用对应的Parser解析器类构造差异化的各种元素
2)LST构造完成后,调用配方(Recipe)中的访问器执行自动化代码重构
不同类型源文件对应的Parser解析器类图如下:
JavaParser:针对Java源文件代码的解析器顶层抽象接口,具体实现子类是针对不同JDK版本的解析器(解析器隔离,解析不同JDK版本的语法特性),比如Java8Parser解析JDK8版本,内部实现委托给ReloadableJava8Parser进行解析,最终将Java源代码文件解析为J.CompilationUnit顶层元素
PropertiesParser:针对Properties属性文件的解析器类,最终解析为Properties.File顶层元素
XmlParser:针对Xml类型文件的解析器类,最终解析为Xml.Document顶层元素
......其它类似
4. 配方(Recipe)执行流程
4.1 执行入口
配方的实际执行入口是:RecipeRun run(LargeSourceSet before, ExecutionContext ctx, int maxCycles, int minCycles)
- LargeSourceSet:表示输入源文件集合封装类,包含配方执行重构的文件列表
- ExecutionContext:配方执行上下文,可以用于全局参数传递等用途
- maxCycles:指定配方最大执行周期次数
- minCycles:指定配方最小执行周期次数
- RecipeRun:封装了配方执行后的结果,内部包含了结果集Changeset
4.2 LargeSourceSet说明
这里展开LargeSourceSet的类图如下,可以看出其内部包含了List<SourceFile>源文件集合:
4.3 配方执行时序图
配方(Recipe)执行过程的时序图细化如下:
其中,RecipeScheduler封装了配方调度执行的具体细节,内部会委托给RecipeRunCycle(配方单次执行的封装对象,最大执行次数可在调度时进行指定)执行实际的源文件改写;
RecipeRunCycle封装了配方单次执行的主体逻辑,其中主要包含了以下3个方法:
1)scanSources
对源文件集合进行前置扫描,通常用于在源文件实际改写前通过扫描源文件集合获取一些上下文信息,用于重构逻辑中辅助判断或者元数据获取
2)generateSources
用于在源文件实际改写前,生成新的源文件,并添加到源文件集合中进而执行后续的文件改写操作
3)editSources
执行实际的源文件改写操作,这里会调用配方(Recipe)中定义的访问器对源文件各具体元素进行visit,执行重构逻辑,该部分也是开发者可以覆写重构逻辑的地方
4.4 配方执行结果表示
配方执行完成后的结果存放到了RecipeRun类中,其中字段changeSet存放了所有变更的结果集List<Result>,对象Result中又保存了变更前(before)和变更后(after)的源文件
最终将重构后(after)的源文件进行输出,完成了自动化重构的处理流程。
相关文章:

OpenRewrite框架原理解析
目录 1. OpenRewrite处理流程概述 2. OpenRewrite访问者模式的应用 2.1 访问者模式简介 2.2 OpenRewrite框架如何应用访问者模式 2.2.1 抽象访问者&具体访问者 2.2.2 抽象元素&具体元素 3. LST无损语义树构造 4. 配方(Recipe)执行流程 …...

LeetCode_Java_递归系列(题目+思路+代码)
206.反转链表 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1]以此类推,直到反转结束返回头结点 class Solution {public ListNode rever…...
c++ 编译为WebAssembly时,怎么判断是release/debug环境?
我对这块研究不深 我的需求是把cpp代码编译为wasm的形式时,需要知道是debug/release 然而 尝试了一些办法 没有满足我的需求 尝试1: #include <iostream>bool isDebugMode() { #ifdef EMSCRIPTENbool isDebug EM_ASM_INT({return (typeof conso…...
信号处理--基于正则化聚合的共空间模态(CSP)脑电信号分类
目录 理论 工具 方法实现 代码获取 参考文献 理论 传统的通用空间模式 (CSP) 是一种流行的算法,用于对脑电图 (EEG) 信号进行分类。本文主要介绍小样本设置 (SSS) 中 CSP 的正则化和聚合技术。传统的 CSP 基于样本协方差矩阵估计。如果训练样本数量较少,其脑电图分类的…...
【2024年5月备考新增】《软考真题分章练习(含答案解析) - 11 项目风险管理(高项)》
1 题目 1、风险可以从不同角度、根据不同的标准来进行分类。百年不遇的暴雨属于()。 A.不可预测风险 B.可预测风险 C.已知风险 D.技术风险 2、人们对风险事件都有一定的承受能力,当()时,人们愿意承担的风险越大。 A.项目活动投入的越多 B.项目的收益越大 C.个人、组织拥…...

【3GPP】【核心网】【4G】4G手机接入过程,手机附着过程(超详细)
1. 4G手机接入过程,手机附着过程 附着(Attach): 终端在PLMN中注册,从而建立自己的档案,即终端上下文 进行附着的三种情况: ①终端开机后的附着,初始附着 ②终端从覆盖盲区返回到…...
【LeetCode-46.全排列】
题目详情: 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1: 输入:nums [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]示例 2: …...

【Web】浅聊Jackson序列化getter的利用——POJONode
目录 核心速览 原理分析 EXP TemplatesImpl利用 SignedObject利用 核心速览 writeValueAsString是jackson序列化自带的入口,在调用该方法的过程中将会通过遍历的方法将bean对象中的所有的属性的getter方法进行调用 下面介绍如下利用链: BadAttrib…...

osgEarth学习笔记2-第一个Osg QT程序
原文链接 上个帖子介绍了osgEarth开发环境的安装。本帖介绍我的第一个Osg QT程序。 下载 https://github.com/openscenegraph/osgQt 解压,建立build目录。 使用Cmake-GUI Configure 根据需要选择win32或者x64,这里我使用win32. 可以看到include和lib路…...

2024年发布jar到国外maven中央仓库最新教程
2024年发布jar到国外maven中央仓库最新教程 文章目录 1.国外sonatype仓库的版本1.1老OSSHR账号注册说明1.2新账号注册说明 2.新账号注册(必选)3.新账号登录创建Namespace3.1创建Namespace的名字的格式要求(必选)3.2发布一个静态网站(可选&…...
在ubuntu22.04.4安装freeswitch1.10.10
一、环境 No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.4 LTS Release: 22.04.4 Codename: jammy 二、依赖 1、 工具包 apt install -y openssh-server …...

qt 置顶窗口崩溃无法退出解决,停止运行快捷键设置
有时置顶窗口调试崩溃需要快捷键进行关闭,如下设置即可 这样就可以通过全局快捷键退出了,避免置顶崩溃无法关闭程序的问题。...

HBCalculator 程序:通过 VMD 可计算分子动力学模拟中氢键密度和强度的一维和二维分布
分享一个通过 VMD 可计算分子动力学模拟中氢键密度和强度的一维和二维分布程序 HBCalculator。 感谢论文的原作者! 主要内容 “氢键是分子系统中关键的非共价相互作用,对生物、化学和能量相关过程产生重大影响;因此,描述氢键信息…...

鸿蒙-项目创建及了解
目录 项目创建 1.App普通项目创建 2.元服务创建 项目结构 .hvigor .idea AppScope entry EntryAbility.ts pages resources module.json5 ohosTest hvigorfile.ts build-profile.json5 oh_modules build-profile.json5 hvigorfile.ts 项目运行 项目创建 F…...

SQLiteC/C++接口详细介绍sqlite3_stmt类(九)
返回:SQLite—系列文章目录 上一篇:SQLiteC/C接口详细介绍sqlite3_stmt类(六) 下一篇: 无 33、sqlite3_column_table_name 函数 sqlite3_column_table_name 用于返回结果集中指定列所属的表的名称。如果查询中列使…...

idea2023 运行多 springboot 实例
概要 1、修改idea运行多实例(本地测试负载) 你可能用到其他 1、改造项目缓存token 至redis 支持负载均衡部署 SpringSecurity6.0RedisJWTMP基于token认证功能开发(源码级剖析可用于实际生产项目)_springsecurity redis管理token…...
HarmonyOS系统开发ArkTS常用组件编程技巧
目录 样式复用 Styles方法 Extend方法 组件编程在使用过程中有很多技巧,在这里分享样式复用技巧和UI结构复用技巧。 样式复用 我们观察下面的代码,在代码中很多重复行的代码,如: Image 的 .width(30).height(30) 是重复的But…...
大数据开发(Hive面试真题-卷三)
大数据开发(Hive面试真题) 1、Hive的文件存储格式都有哪些?2、Hive的count的用法?3、Hive得union和unionall的区别?4、Hive的join操作原理,left join、right join、inner join、outer join的异同࿱…...
Oracle数据库SQL开发规范
Oracle数据库SQL开发规范是为了保证SQL代码的质量、可读性和性能而遵循的一系列准则和最佳实践。以下是一些常见的Oracle SQL开发规范要点: 1. 命名规范 使用有意义且一致的命名约定,例如表名采用TBL_MODULE_NAME,视图采用VW_MODULE_VIEW等…...

FreeRTOS 消息队列
1. 队列简介 1.1 队列的概念 队列是任务到任务、任务到中断、中断到任务数据交流的一种机制(消息传递) 类似全局变量?假设有一个全局变量a 0,现有两个任务都在写这个变量 a: 大家想象一下如果任务 1 运行一次&#…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...