北京丰台做网站营销手段有哪些方式
目录
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)的源文件进行输出,完成了自动化重构的处理流程。