MeterSphere 之 Idea插件开发

前言:
Metersphere 官网支持IDEA插件可以一键导入到MS当中,但一些项目当中自己定义的注解并不支持解析,所以基于这个场景的考虑,自己准备适配一下自定义插件的解析。

官网插件地址:https://github.com/metersphere/metersphere-idea-plugin


一 、MAC 安装gradle

去官网看到插件的结构,是通过java编写,gradle作为构建工具。平时使用Maven比较多,所以这边从0开始搭建一个插件环境。

  1. 跟使用Maven一样,直接去官方 https://gradle.org/install/ 查看下载适合自己的方式。我使用安装包下载。安装之后解压,配置PATH环境变量即可。

image.png

vi ~/.zshrc (我本地安装了item2,所以是这个文件。)
image.png

验证 gradle --version
image.png
注意:安装gradle先安装jdk,安装jdk网上资料很多。
具体的用法不在这里介绍了,直接查看官网即可。
参考:
https://docs.gradle.org/7.4.2/userguide/what_is_gradle.html
https://www.iteye.com/blog/shmilyaw-hotmail-com-2345439

二、使用gradle构建IDEA插件环境

2.1 跟着官网走

  1. IDEA 官方是有支持的:https://plugins.jetbrains.com/docs/intellij/welcome.html
  2. 官网有插件模板:https://github.com/JetBrains/intellij-platform-plugin-template

image.png
官网模板是kotlin写的。

可以先看下整体的目录结构

.
├── .github/                GitHub Actions workflows and Dependabot configuration files
├── .run/                   Predefined Run/Debug Configurations
├── gradle
│   └── wrapper/            Gradle Wrapper
├── build/                  Output build directory
├── src                     Plugin sources
│   └── main
│       ├── kotlin/         Kotlin production sources  存放项目的源码
│       └── resources/      Resources - plugin.xml, icons, messages  存放项目的资源配置,图标,信息等
│   └── test
│       ├── kotlin/         Kotlin test sources
│       └── testData/       Test data used by tests
├── .gitignore              Git ignoring rules
├── build.gradle.kts        Gradle configuration
├── CHANGELOG.md            Full change history
├── gradle.properties       Gradle configuration properties
├── gradlew                 *nix Gradle Wrapper script
├── gradlew.bat             Windows Gradle Wrapper script
├── LICENSE                 License, MIT by default
├── qodana.yml              Qodana configuration file
├── README.md               README
└── settings.gradle.kts     Gradle project settings

plunig.xml

image.png

<idea-plugin>
  <id>org.jetbrains.plugins.template</id>
  <name>Template</name>
  <vendor>JetBrains</vendor>
  <depends>com.intellij.modules.platform</depends>

  <extensions defaultExtensionNs="com.intellij">
    <applicationService serviceImplementation="..."/>
    <projectService serviceImplementation="..."/>
  </extensions>

  <projectListeners>
    <listener class="..." topic="..."/>
  </projectListeners>
</idea-plugin>

了解了项目架构之后,开始自己搭建一个环境。

2.2本地搭建环境

  1. 前面我们已经安装好了gradle,然后我们打开idea. 新建项目,选择gradle。
  2. 注意,因为我们是要用java开发的插件,所以我们要勾选两个,java和intelliJ platform plugin

image.png

新建之后的目录结构是这样子的:

image.png

src
  --main
    --java //   一般我们写的java文件都会放到这个里面。
    --resources 
      --META-INF
        --plugin.xml   // 插件的配置文件
        
build.gradle // 构建gradle的配置文件,相当于maven里面的pom.xml文件,我们所需要的依赖信息都会放到这里面来。

**plugin.xml **

<idea-plugin>

  <!-- 插件名称,别人在官方插件库搜索你的插件时使用的名称 -->
  <name>MyPlugin</name>

  <!-- 插件唯一id,不能和其他插件项目重复,所以推荐使用com.xxx.xxx的格式
       插件不同版本之间不能更改,若没有指定,则与插件名称相同 -->
  <id>com.example.plugin.myplugin</id>

  <!-- 插件的描述 -->
  <description>my plugin description</description>

  <!-- 插件版本变更信息,支持HTML标签;
       将展示在 settings | Plugins 对话框和插件仓库的Web页面 -->
  <change-notes>Initial release of the plugin.</change-notes>

  <!-- 插件版本 -->
  <version>1.0</version>

  <!-- 供应商主页和email-->
  <vendor url="http://www.jetbrains.com" email="support@jetbrains.com" />

  <!-- 插件所依赖的其他插件的id -->
  <depends>MyFirstPlugin</depends>

  <!-- 插件兼容IDEA的最大和最小 build 号,两个属性可以任选一个或者同时使用
       官网详细介绍:http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html-->
  <idea-version since-build="3000" until-build="3999"/>

  <!-- application components -->
  <application-components>
    <component>
      <!-- 组件接口 -->
      <interface-class>com.foo.Component1Interface</interface-class>
      <!-- 组件的实现类 -->
      <implementation-class>com.foo.impl.Component1Impl</implementation-class>
    </component>
  </application-components>

  <!-- project components -->
  <project-components>
    <component>
      <!-- 接口和实现类相同 -->
      <interface-class>com.foo.Component2</interface-class>
    </component>
  </project-components>

  <!-- module components -->
  <module-components>
    <component>
      <interface-class>com.foo.Component3</interface-class>
    </component>
  </module-components>

  <!-- Actions -->
  <actions>
    ...
  </actions>

  <!-- 插件定义的扩展点,以供其他插件扩展该插件 -->
  <extensionPoints>
    ...
  </extensionPoints>

  <!-- 声明该插件对IDEA core或其他插件的扩展 -->
  <extensions xmlns="com.intellij">
    ...
  </extensions>
</idea-plugin>

到此,插件的环境已经搭建好了。

三、基于MS官方插件适配自定义注解

IDEA 插件官方提供了一些PSI接口,可以方便我们分析源代码文件。

3.1 PSI介绍

image.png
官方介绍: 程序结构接口,通常被称为PSI,是IntelliJ 平台中负责解析文件和创建语法和语义代码模型的层,改模型支持平台的许多功能。

简单理解就是我们要想分析源代码文件的内容,就需要PSI的帮助。

我们知道,JVM在加载类之前,首先需要读取Class文件,并将Class文件解析成一个结构体对象,对应的是Class文件结构。与JVM解析Class文件不同的是,IDEA解析的Java的源代码,但IDEA也是将Java文件解析为一个结构体对象。
请记住一句话,对于任何拥有固定结构的文件或者代码,都可以使用访问者模式。
不仅Java文件,任何代码文件都会有一定的结构,否则编译器也不能识别,也是因为如此,IDEA实现的PSI与Java字节码操作工具ASM有非常多的相似之处,除了都是将文件解析成结构外,也都支持使用访问者模式编辑文件,一个大的结构下面包含许多小的结构,小的结构也支持使用访问者模式编辑。
因为很相似,所以我们可以用学习使用 ASM 工具分析、创建、或改写Class文件的思维去学习PSI。
由于不同的编程语言编写的代码文件有不同的结构,IDEA将文件结构抽象为接口,叫程序结构接口文件(PSI File),不同类型的文件解析后生成不同的PsiFile接口的实现类实例,这也是IDEA能够扩展支持多语言的基础。

3.1.1 PsiFile 接口

一个文件就是一个PsiFile,也是一个文件的结构树的根节点,PsiFile是一个接口,如果文件是一个.java文件,那么解析生成的PsiFile就是PsiJavaFile对象,如果是一个Xml文件,则解析后生成的是XmlFile对象。

3.1.2 PsiElement接口

Class文件结构包含字段表、属性表、方法表等,每个字段、方法也都有属性表,但在PSI中,总体上只有PsiFile和PsiElement。
Element即元素,一个PsiFile(本身也是PsiElement)由许多的PsiElement构成,每个PsiElement也可以由许多的PsiElement构成。
PsiElement用于描述源代码的内部结构,不同的结构对应不同的实现类。
对应Java文件的PsiElement种类有:PsiClass、PsiField、PsiMethod、PsiCodeBlock、PsiStatement、PsiMethodCallExpression等等。其中,PsiField、PsiMethod都是PsiClass的子元素,PsiCodeBlock是PsiMethod的子元素,PsiMethodCallExpression是PsiCodeBlock的子元素,正是这种关系构造成了一棵树。

解析一个Java文件有上百种类型的PsiElement,对于一个新手,我们如何才能快速的认识对应Java代码文件中的每行代码都会解析生成呢?好在IDEA提供了PSI视图查看器。
如果你正在编写插件,那么IDEA会自动在“工具”菜单中显示“查看PSI结构”的选项,否则,我们需要修改IDEA的配置文件才能在“工具”菜单中看到这个选项。
工具
image.png
配置文件在IDEA安装路径的bin目录下,找到idea.properties文件,如下图所示。
image.png

添加配置后重启IDEA就能看到tools菜单下新加了两个选择,如下图所示。
image.png
其中View PSI Structure of Current File是将当前查看的文件解析为结构树,选中选项后弹出如下图所示的窗口。

image.png
image.png

  • Show PSI structure for:选择PsiFile类型;
  • Show PsiWhiteSpace:去掉勾选后可以隐藏表示连续空格(包括换行符)的元素PsiElement;

当我们选中源码时,IDEA会找到对应的PsiElement标志为选中状态,如上图左侧的PSI Tree窗口所示
这个小工具可以帮我们刚好的理解PSI的源码

3.1.3 PsiReference 接口

一个PsiReference表示代码中某个PsiElement链接到相应的声明。
简单理解,PsiReference就是我们选中鼠标右键弹出菜单中Go To的Declaration or Usages、或者按住command键+鼠标点击后能够跳转到相应声明的依据。
image.png

理解了核心的几个接口,在IDEA的PSI中,有很多工具类可以帮助我们在写插件的时候事半功倍。

image.png
源码当中写的都比较清楚。具体的可以研究下。

3.2 MS插件源码解析

在介绍MS分析源码之前先看下效果。
具体用法参考官网
https://github.com/metersphere/metersphere-idea-plugin
image.png

image.png

image.png
结果
image.png
针对业务开发人员来讲,就是一个idea的插件,通过对接Metersphere,将项目中的Controller 一键导出到Metersphere当中,完美的融化了开发部门和测试部门的配合。

  • 效果看完之后我们开始解析

首先去官网clone 到本地 https://github.com/metersphere/metersphere-idea-plugin
项目结构:
image.png
目前MS支持两种导出方式:1. postman 2. metersphere

  • action : 触发事件的类。
  • gui: gui的配置页面
  • exporter:两种类型的导出代码
  • 其他目录:都是其它工具类和配置

3.2.1 看一个插件从 plugin.xml 开始

image.png

这个配置文件是这个插件的整体配置信息,可以从中获取到插件的ID,名称,邮箱等基本信息和Action以及扩展插件的信息,具体意思参考上面的本地环境搭建。

  • 从MS 当中可以看到,这个插件有两个action和两个service 。
    • Service 是一个Component,可以理解成一个容器。
    • Action 可以理解成是一个事件触发器,比如在IDEA中导出的动作,就可以是一个action。

3.2.2 AppSettingService

image.png

  1. PersistentStateComponent 这个是个接口,是用来持久化存储数据的。上面的注解是指,在运行插件的时候储存的类型。

3.2.3 AppSettingConfigurable

image.png

  1. 实现了Configurable接口,初始化配置,这里初始化的是gui的配置文件信息。

3.2.4 AppSettingComponent和AppSettingComponent.form

image.png

image.png

  1. 这两个是一一对应的,在from当中,只有写了fieldName的值,才会在AppSettingComponent类中显示属性,例如:JPanel,JTextField,JPasswordField,JTabbedPane,JComboBox等。

image.png

  1. AppSettingComponent() 这个方法是个构造方法,其中initData(appSettingState)是初始化配置信息的方法,可以看到里面很多Listener,这些个Listener是对gui填写内容的监听,每个类型的Listener不同,但监听原理是相同的。都是当内容发生的时候,触发的动作。

3.2.5 以MS的导出解析:ExportToMSAction

image.png

继承了CommonAction
image.png
image.png
所以核心类就是MeterSphereExporter这个类
image.png

这个方法的整体逻辑就是

  1. 先过滤出所有的PsiJavaFile文件,然后通过调用postmanExporter.transform() 方法去进行解析,然后再构建需要导出的文件格式,通过调用MS的API接口导入到MS当中。所以我们的重点在transform这个方法。

3.2.6 Transform

image.png

  1. 这边必须要注意:导出的接口要符合restful api 风格,要不然不支持的。

image.png

  1. 先判断类上面是否有RequestMapping,用来构建该Controller的访问路径。
  2. 然后迭代该类中所有方法,进行构建json schema。

image.png

  1. 其实源码当中注释的挺清楚的,按照一个接口需要哪些部分组成的顺序往下读代码就行了:url, header,body等
  2. 从注解开始,所以在每个方法解析之前获取注解上面的请求类型比如:GET,POST等。

image.png

  1. 前面的都很好理解,主要是body的解析。
  2. 校验注解只要含有 RequestBody、RequestParam、RequestPart、RequestBody、RequestPart还有servlet的httpRequest和httpResponse 的。都会被解析。
  3. 这边主要说@RequestBody 的解析方法 getRaw()

image.png

  1. 构建导出对象,然后开始渲染,有一个点需要注意,这个使用的语法是Json Scheam 的语法,熟悉了Json Scheam 会对整个构建过程有一个很清晰的认识。

参考链接:Json Schema 官方网站 中文文档 生成Json Schema 工具
image.png

  1. 红框标注的是意思为:根据当前类型的全限定名,在全局project当中获取对应的类型PsiClass。
  2. 注意:此方法只能解析对象,数组,基本数据类型等。如果是泛型,例如:Request这种,解析不了,PsiClass为null。

image.png

  1. 构建json schema 的数据类型方法,如果有javadoc的注释,以javadoc的注释为主。
  2. 如果PsiClass为null,则走else的方法。

image.png
image.png

  1. 获取类的所有字段,然后循环开始读取,构建schema数据。
  2. 以此类推,获取字段,判断类型,构建json schema数据,递归循环。

image.png

  1. 前面解析完之后,接下来构建导入MS所需要的数据格式,然后生成一个临时文件。

image.png

  1. 调用MS的导入接口,导入该文件即可。

3.2.7 整体逻辑

  1. 获取所有的PsiJavaFile。
  2. 解析符合restful风格的类。
  3. 判断javadoc,判断controller 的访问路径,
  4. 获取url
  5. 获取header
  6. 获取body
    1. 循环方法参数。
    2. 判断类型。
      1. 简单类型
        1. 循环所有属性。
          1. 判断类型
          2. 构建json schema
          3. … 不断递归循环
      2. 复杂类型
        1. 循环属性。
        2. 是否为泛型。
          1. 分层解析。先解析外层,在解析内层
            1. 都是判断类型,构建json schema .
            2. …不断递归循环。
  7. 构建需要导出的格式,生成导入文件。
  8. 请求MS导入接口。
  9. 返回上传结果。
  10. 刷新MS接口定义列表,查看结果。

3.3 添加自定义注解

当前测试下来,自定义注解是不支持的,还有复杂的泛型嵌套,也是不支持的。所以基于客户的需求,重构了一下代码。

  1. 新增加一个resolver 文件夹。写了一个构建自定义注解的工厂。

image.png

  1. 重构了getRaw中的 对象解析

image.png

  1. 在构建properties 的scheam 中添加解析自定义注解方法

image.pngimage.png

经过一番改造后,最后终于支持自定义注解了,并且支持复杂的泛型嵌套。比如

  1. Request
  2. Response
  3. 还有自定义注解:CommonAnnotation、CustNoAnnotation、CertNoAnnotation、CertTypeAnnotation

四、写在最后

  1. 初次上手中间还是遇到了很多坑的,不过等写完之后,还是有一点点成就感的。
  2. 静下心来,多研究下源码,去github也找一些插件研究一下。
  3. 虽然当前插件功能不是很完善,但目前内部业务基本都支持了。
  4. 以上内容仅个人学习心得,如有错误,欢迎指正。
  5. 整理下参考的资料:
    1. 官网插件地址:https://github.com/metersphere/metersphere-idea-plugin
    2. gradle官方: https://gradle.org/install/
    3. IDEA 官方是有支持的:https://plugins.jetbrains.com/docs/intellij/welcome.html
    4. 官网有插件模板:https://github.com/JetBrains/intellij-platform-plugin-template
    5. idea插件介绍:https://cloud.tencent.com/developer/article/1348741
    6. 参考插件:
      1. https://github.com/docer-savior/docer-savior-idea-plugin
      2. https://github.com/tangcent/easy-yapi
    7. 源码地址:https://github.com/hao65103940/metersphere-idea-plugin/tree/master-fg
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值