自定义注解与拦截器实现不规范sql拦截(自定义注解填充插件篇)

在自定义注解与拦截器实现不规范sql拦截(拦截器实现篇)中提到过,写了一个idea插件来辅助对Mapper接口中的方法添加自定义注解,这边记录一下插件的实现。

需求简介

在上一篇中,定义了一个自定义注解对需要经过where判断的Mapper sql方法进行修饰。那么,现在想使用一个idea插件来辅助进行自定义注解的增加,需要做到以下几点:

  1. 支持在接口名带Mapper的编辑页面中,右键菜单,显示增加注解信息的选项
  2. 鼠标移动到该选项,支持显示可选的需要新增的注解名称
  3. 点击增加,对当前Mapper中的所有方法增加对应注解;同时,没有import的文件中需要增加对应的包导入。

具体实现

插件开发所需前置

第一点就是需要gradle进行打包,所以需要配置gradle项目和对应的配置文件;第二点就是在Project Structure中,将SDK设置为IDEA的sdk,从而导入支持对idea界面和编辑内容进行处理的api。idea大多数版本本身就会提供plugin开发专用的project,对应的配置文件会在project模板中初始化,直接用就行。

插件配置文件

plugin.xml,放在reources的META-INF元数据文件夹下,自动进行插件基本信息的读取:

<!-- Plugin Configuration File. Read more: https://plugins.jetbrains.com/docs/intellij/plugin-configuration-file.html -->
<idea-plugin><!-- Unique identifier of the plugin. It should be FQN. It cannot be changed between the plugin versions. --><id>com.huiluczp.checkAnnocationPlugin</id><!-- Public plugin name should be written in Title Case.Guidelines: https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-name --><name>CheckAnnocationPlugin</name><!-- A displayed Vendor name or Organization ID displayed on the Plugins Page. --><vendor email="970921331@qq.com" url="https://www.huiluczp.com">huiluczP</vendor><!-- Description of the plugin displayed on the Plugin Page and IDE Plugin Manager.Simple HTML elements (text formatting, paragraphs, and lists) can be added inside of <![CDATA[ ]]> tag.Guidelines: https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-description --><description>Simple annotation complete plugin used for mybatis mapping interface.</description><!-- Product and plugin compatibility requirements.Read more: https://plugins.jetbrains.com/docs/intellij/plugin-compatibility.html --><depends>com.intellij.modules.platform</depends><depends>com.intellij.modules.lang</depends><depends>com.intellij.modules.java</depends><!-- Extension points defined by the plugin.Read more: https://plugins.jetbrains.com/docs/intellij/plugin-extension-points.html --><extensions defaultExtensionNs="com.intellij"></extensions><actions><group id="add_annotation_group" text="Add Self Annotation" popup="true"><!-- EditorPopupMenu是文件中右键会显示的菜单 --><add-to-group group-id="EditorPopupMenu" anchor="last"/><action id="plugin.demoAction" class="com.huiluczp.checkannotationplugin.AnnotationAdditionAction" text="@WhereConditionCheck"description="com.huiluczP.annotation.WhereConditionCheck"></action></group></actions>
</idea-plugin>

对插件功能实现来说,主要需要关注的是actions部分,其中,设置了一个名为add_annotation_group的菜单组,在这个标签中,使用add-to-group标签将其插入EditorPopupMenu中,也就是右键展开菜单。最后,在我们定义的菜单组中,增加一个action,也就是点击后会进行对应功能处理的单元,在class中设置具体的实现类,并用text设置需要显示的信息。

功能类实现

将所有功能都塞到了AnnotationAdditionAction类中。

public class AnnotationAdditionAction extends AnAction {private Project project;private Editor editor;private String annotationStr;private AnActionEvent event;private String fullAnnotationStr;@Override// 主方法,增加对应的注解信息public void actionPerformed(AnActionEvent event) {project = event.getData(PlatformDataKeys.PROJECT);editor = event.getRequiredData(CommonDataKeys.EDITOR);// 获取注解名称annotationStr = event.getPresentation().getText();fullAnnotationStr = event.getPresentation().getDescription();// 获取// 获取所有类PsiClass[] psiClasses = getAllClasses(event);// 对类中所有满足条件的类增加Annotationfor(PsiClass psiClass:psiClasses){// 满足条件List<String> methodNames = new ArrayList<>();if(checkMapperInterface(psiClass)) {PsiMethod[] psiMethods = psiClass.getMethods();for (PsiMethod psiMethod : psiMethods) {PsiAnnotation[] psiAnnotations = psiMethod.getAnnotations();boolean isExist = false;System.out.println(psiMethod.getName());for (PsiAnnotation psiAnnotation : psiAnnotations) {// 注解已存在if (psiAnnotation.getText().equals(annotationStr)){isExist = true;break;}}// 不存在,增加信息if(!isExist){System.out.println("add annotation "+annotationStr + ", method:" + psiMethod.getName());methodNames.add(psiMethod.getName());}}}// 创建线程进行编辑器内容的修改// todo 考虑同名,还需要考虑对方法的参数判断,有空再说吧WriteCommandAction.runWriteCommandAction(project, new TextChangeRunnable(methodNames, event));}}

实现类需要继承AnAction抽象类,并通过actionPerformed方法来执行具体的操作逻辑。通过event对象,可以获取idea定义的project项目信息和editor当前编辑窗口的信息。通过获取当前窗口的类信息,并编辑对应文本,最终实现对所有满足条件的方法增加自定义注解的功能。

    // 获取对应的method 并插入字符串class TextChangeRunnable implements Runnable{private final List<String> methodNames;private final AnActionEvent event;public TextChangeRunnable(List<String> methodNames, AnActionEvent event) {this.methodNames = methodNames;this.event = event;}@Overridepublic void run() {String textNow = editor.getDocument().getText();StringBuilder result = new StringBuilder();// 考虑import,不存在则增加import信息PsiImportList psiImportList = getImportList(event);if(!psiImportList.getText().contains(fullAnnotationStr)){result.append("import ").append(fullAnnotationStr).append(";\n");}// 对所有的方法进行定位,增加注解// 粗暴一点,直接找到public的位置,前面增加注解+\nString[] strList = textNow.split("\n");for(String s:strList){boolean has = false;for(String methodName:methodNames) {if (s.contains(methodName)){has = true;break;}}if(has){// 获取当前行的缩进int offSet = calculateBlank(s);result.append(" ".repeat(Math.max(0, offSet)));result.append(annotationStr).append("\n");}result.append(s).append("\n");}editor.getDocument().setText(result);}// 找到字符串第一个非空字符前空格数量private int calculateBlank(String str){int length = str.length();int index = 0;while(index < length && str.charAt(index) == ' '){index ++;}if(index >= length)return -1;return index;}}

需要注意的是,在插件中对文本进行编辑,需要新建线程进行处理。TextChangeRunnable线程类对当前编辑的每一行进行分析,保留对应的缩进信息并增加public方法的自定义注解修饰。同时,判断import包信息,增加对应注解的import。

    @Override// 当文件为接口,且名称中包含Mapper信息时,才显示对应的右键菜单public void update(@NotNull AnActionEvent event) {super.update(event);Presentation presentation = event.getPresentation();PsiFile psiFile = event.getData(PlatformDataKeys.PSI_FILE);presentation.setEnabledAndVisible(false); // 默认不可用if(psiFile != null){VirtualFile virtualFile = psiFile.getVirtualFile();FileType fileType = virtualFile.getFileType();// 首先满足为JAVA文件if(fileType.getName().equals("JAVA")){// 获取当前文件中的所有类信息PsiClass[] psiClasses = getAllClasses(event);// 只允许存在一个接口类if(psiClasses.length!=1)return;for(PsiClass psiClass:psiClasses){// 其中包含Mapper接口即可boolean isOk = checkMapperInterface(psiClass);if(isOk){presentation.setEnabledAndVisible(true);break;}}}}}

重写update方法,当前右键菜单显示时,判断是否为接口名带Mapper的情况,若不是则进行自定义注解增加功能的隐藏。

    // 获取当前文件中所有类private PsiClass[] getAllClasses(AnActionEvent event){PsiFile psiFile = event.getData(PlatformDataKeys.PSI_FILE);assert psiFile != null;FileASTNode node = psiFile.getNode();PsiElement psi = node.getPsi();PsiJavaFile pp = (PsiJavaFile) psi;return pp.getClasses();}// 获取所有import信息private PsiImportList getImportList(AnActionEvent event){PsiFile psiFile = event.getData(PlatformDataKeys.PSI_FILE);assert psiFile != null;FileASTNode node = psiFile.getNode();PsiElement psi = node.getPsi();PsiJavaFile pp = (PsiJavaFile) psi;return pp.getImportList();}// 判断是否为名称Mapper结尾的接口private boolean checkMapperInterface(PsiClass psiClass){if(psiClass == null)return false;if(!psiClass.isInterface())return false;String name = psiClass.getName();if(name == null)return false;return name.endsWith("Mapper");}

最后是几个工具方法,通过psiFile来获取对应的psiJavaFile,从而得到对应的类信息。

插件打包

因为使用了gradle,直接使用gradle命令进行打包。

gradlew build

之后会自动执行完整的编译和打包流程,最终会在/build/distributions文件夹下生成对应的jar文件。
在这里插入图片描述
在这里插入图片描述
之后,在idea的settings中搜索plugins,点击配置中的本地install选项,即可选择并加载对应的插件jar。
在这里插入图片描述

效果展示

创建一个简单的UserMapper类。

public interface UserMapper {public String queryG();public String queryKKP();
}

在编辑页面上右键显示菜单,点击我们之前设置的新按钮增加自定义注解信息,增加成功。
在这里插入图片描述

在这里插入图片描述

总结

这次主要是记录了下简单的idea插件开发过程,idea的sdk以编辑页面为基础提供了PSI api来对当前页面与整体项目的展示进行修改,还是挺方便的。配置文件对action展示的位置进行编辑,感觉和传统的gui开发差不多。
对现在这个插件,感觉还可以拓展一下编辑界面,输进其他想增加的注解类型和展示逻辑,有空再拓展吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/639769.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[足式机器人]Part2 Dr. CAN学习笔记- Kalman Filter卡尔曼滤波器Ch05

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记 - Kalman Filter卡尔曼滤波器 Ch05 1. Recursive Algirithm 递归算法2. Data Fusion 数据融合Covarince Matrix协方差矩阵State Space状态空间方程 Observation观测器3. Step by step : Deriatio…

【Java】面向对象之继承超级详解!!

文章目录 前言一、继承1.1 继承的概念1.1.1继承的语法 1.2 父类成员访问1.2.1 子类中访问父类的成员变量1.子类和父类中没有同名的成员变量2.子类和父类中有同名的成员变量 1.2.2子类中访问父类的成员方法成员方法名不同成员方法名字相同 1.3 super关键字1.4 子类构造方法1.5 继…

Python零基础教程5.1——Python官方自带Turtle.demo

官方装13最为致命 牛&#xff01;Python自带画图demo引言DEMO有什么&#xff1f;总结 牛&#xff01;Python自带画图demo 引言 我的电脑不是换新了嘛 所以 不得不重新下载Python 这一下 不得了 我下载了Python3.11.7这个版本 然后按照惯例 打开IDEL 平平无奇 但 我一不小心…

特斯拉开年再降价,2024年的汽车市场还会好吗?

“等等派”再度胜利&#xff01;1月12日&#xff0c;特斯拉中国官宣Model 3和Model Y降价。其中&#xff0c;Model 3焕新版下降15500元&#xff0c;Model 3长续航焕新版下调11500元&#xff1b;特斯拉Model Y后轮驱动版售价下调7500元&#xff0c;特斯拉Model Y长续航版售价下调…

《GitHub Copilot 操作指南》课程介绍

第1节&#xff1a;GitHub Copilot 概述 一、什么是 GitHub Copilot 什么是 GitHub Copilot GitHub Copilot是GitHub与OpenAI合作开发的编程助手工具&#xff0c;利用机器学习模型生成代码建议。它集成在开发者的集成开发环境&#xff08;IDE&#xff09;中&#xff0c;可以根…

Excel象限图

Excel象限图 1、背景描述2、象限图&#xff08;散点图&#xff09;3、象限图&#xff08;气泡图&#xff09; 1、背景描述 平常我们在工作中做图表时&#xff0c;使用最多的就是柱状形、折线图、饼图&#xff0c;这些图表主要为了展示趋势、对比和构成&#xff0c;但有时候我们…

文本生成中的解码器方法

一.解码器的基本介绍 在文本生成任务中&#xff0c;解码器是生成序列的关键组件。解码器的目标是从先前生成的标记或隐藏状态中生成下一个标记。有几种方法用于设计文本生成中的解码器&#xff0c;以下是一些常见的解码器方法&#xff1a; Teacher Forcing&#xff08;教师强…

自己动手造一个状态机

自己动手造一个状态机 引言有限自动状态机 (FSM)五要素应用场景优势 开源产品造个轮子改造点Looplab fsm示例演示实现解析 改造过程 引言 有限自动状态机 (Finite-state machine , FSM) 通常用来描述某个具有有限个状态的对象&#xff0c;并且在对象的生命周期中组成了一个状态…

flink基本概念

1. Flink关键组件: 这里首先要说明一下“客户端”。其实客户端并不是处理系统的一部分&#xff0c;它只负责作业的提交。具体来说&#xff0c;就是调用程序的 main 方法&#xff0c;将代码转换成“数据流图”&#xff08;Dataflow Graph&#xff09;&#xff0c;并最终生成作业…

堆详解与优先级队列

导言&#xff1a; 我们知道队列是一种先进先出(FIFO)的数据结构&#xff0c;但是现实情况中&#xff0c;操作的数据有可能会有优先级&#xff0c;优先级高的数据要先出队。例如&#xff0c;医院的军人优先等等。而为此应运而生的就是优先级队列&#xff0c;java中可以使用Prio…

力扣hot100 相交链表 超全注释 满级表达

Problem: 160. 相交链表 文章目录 思路复杂度&#x1f496; Ac Code 思路 &#x1f468;‍&#x1f3eb; 参考题解 &#x1f469;‍&#x1f3eb; 参考图解 复杂度 时间复杂度: O ( n m ) O(nm) O(nm) 空间复杂度: 添加空间复杂度, 示例&#xff1a; O ( 1 ) O(1) O(…

详谈c++智能指针!!!

文章目录 前言一、智能指针的发展历史1.C 98/03 的尝试——std::auto_ptr2.std::unique_ptr3.std::shared_ptr4.std::weak_ptr5.智能指针的大小6.智能指针使用注意事项 二、智能指针的模拟实现三、C11和boost中智能指针的关系 前言 C/C 语言最为人所诟病的特性之一就是存在内存…

Docker是什么

docker本质 Docker 本质其实是 LXC 之类的增强版&#xff0c;它本身不是容器&#xff0c;而是容器的易用工具。容器是 linux 内核中的技术&#xff0c;Docker 只是把这种技术在使用上简易普及了。Docker 在早期的版本其核心就是 LXC 的二次封装发行版。 Docker 作为容器技术的…

开发第一个Flutter App需要注意什么

Flutter这些年发展的很快&#xff0c;特别是在 Google 持续的加持下&#xff0c;Flutter SDK 的版本号已经来到了 3开头&#xff0c;也正式开始对 Windows、macOS 和 Linux 桌面环境提供支持。如果从 Flutter 特有的优势来看&#xff0c;我个人认为主要是它已经几乎和原生的性能…

换手机后:旧手机备忘录怎么导入新手机里?

现在新手机层出不穷&#xff0c;大家都爱换手机来体验新功能&#xff0c;但在换手机的时候&#xff0c;数据传输是非常麻烦的一件事情。 每次换手机&#xff0c;就像是搬一次家。老房子里的点点滴滴&#xff0c;那些重要的、不重要的&#xff0c;都得一一打包&#xff0c;再在…

DSP Bootloader

DSP Bootloader Refer: DSP Bootloader开发思路讲解

字符串展开(Python)

展开字符串中用-压缩的连续小写字母或者数字&#xff0c;不是压缩形式的-不用理会&#xff0c;-没有压缩字符的去除-。 (笔记模板由python脚本于2024年01月21日 18:18:19创建&#xff0c;本篇笔记适合熟悉 p y t h o n python python字符串和列表的coder翻阅) 【学习的细节是欢…

SAP屏幕开发之Listbox下拉列表

文章目录 前言一、案例介绍二、静态下拉列表 a.绘制并设置属性 b.两种属性区别以及效果展示 三、动态下拉列表 a.绘制下拉列表 b.调用函数绑定 四、总结 前言 这篇文章给大家介绍一下SAP Dialog程序中 Listbox控件 的使用&#xf…

如何搭建MariaDB并实现无公网ip环境远程连接本地数据库

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 1. 配置MariaDB数据库1.1 安装MariaDB数据库1.2 测试局域网内远程连接 2. 内网穿透2.1 创建隧道映射…