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

news2024/11/16 12:50:50

在自定义注解与拦截器实现不规范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);

        // 对类中所有满足条件的类增加Annotation
        for(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;
        }

        @Override
        public 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的位置,前面增加注解+\n
            String[] 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.coloradmin.cn/o/1402679.html

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

相关文章

【Coding】寒假每日一题Day.5. 互质数的个数

题目来源 题目来自于AcWing平台&#xff1a;https://www.acwing.com/problem/content/4971/ 以blog的形式记录程序设计算法学习的过程&#xff0c;仅做学习记录之用。 题目描述 输入输出格式与数据范围 样例 第一组&#xff1a; 2 516第二组&#xff1a; 12 711943936思路…

LINUX常用工具之sudo权限控制

一、Sudo基本介绍 sudo是Linux 中用于允许特定用户以超级用户或其他特权用户的身份执行特定的命令或任务。sudo 提供了一种安全的方法&#xff0c;使用户能够临时获取额外的权限&#xff0c;而不需要以完全超级用户的身份登录系统。sudo也可以用了设置黑名单命令清单&#xff…

实战 | SQL注入思路扩展

本文由掌控安全学院 - sbhglqy 投稿 一、资产搜集 我们都知道sql注入的传参有些是明文的&#xff0c;有些是经过编码或者加密的&#xff0c;所以我们搜索的时候不要仅限于inurl:.php?id1&#xff0c;可以额外的尝试搜搜1的base64编码值MQ&#xff0c;即可以搜索inurl:.php?…

万界星空科技MES系统的生产管理流程

对于生产型工厂来说&#xff0c;车间生产流程无疑是最重要的管理环节&#xff0c;繁琐的生产细节让企业很难找到合理的生产管理方法&#xff0c;导致人工效率低、错误多、成本高。如果想要解决这些问题&#xff0c;工厂就必须要有一套自己的生产管理系统&#xff0c;这样才能提…

(2021|ICLR,扩散先验,VE-SDE,逼真和忠实的权衡)SDEdit:使用随机微分方程引导图像合成和编辑

SDEdit: Guided Image Synthesis and Editing with Stochastic Differential Equations 公和众和号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2. 背景&#xff1a;使用随机微分方程…

【HarmonyOS】体验鸿蒙电商平台的未来之旅!

从今天开始&#xff0c;博主将开设一门新的专栏用来讲解市面上比较热门的技术 “鸿蒙开发”&#xff0c;对于刚接触这项技术的小伙伴在学习鸿蒙开发之前&#xff0c;有必要先了解一下鸿蒙&#xff0c;从你的角度来讲&#xff0c;你认为什么是鸿蒙呢&#xff1f;它出现的意义又是…

点燃航天热情,莞港澳青少年航天研学冬令营收获满满

为进一步促进港澳青少年深度了解祖国的最新发展&#xff0c;通过亲身体验增加对祖国的认识&#xff0c;增强对祖国的向心力。“筑梦航天&#xff0c;行进湾区”莞港澳青少年交流实践活动收官之作“航天研学冬令营”于1月20日在东莞成功举办。来自香港、澳门、广州、深圳、东莞各…

AI 欺诈事件频出,如何重塑身份认证的安全性?

据报告表示&#xff0c;生成式人工智能每年可为世界经济注入相当于 4.4 万亿美元的资金。预计到 2030 年&#xff0c;人工智能对全球财政的潜在贡献将达到 15.7 万亿美元。人们惊叹于 AI 强大工作效率&#xff0c;期待能帮忙节省不必要的劳动力&#xff0c;但事实上 AI 出现之后…

216. 组合总和 III - 力扣(LeetCode)

题目描述 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 输入示例 k 3, n 7输出示例 [[1,2,…

Allegro如何导入芯片的Pin Delay?

Allegro在做等长时,需要导入芯片的Pin Delay才能做真正的等长。因为有些芯片内部的引脚本身就是不等长的,例如海思的部分芯片。 那么如何导入芯片的Pin Delay呢? 1、打开约束管理器,点击Properties(属性)→Component(器件)→Pin Properties→General。 在右栏找到芯片U1,…

【Linux】第三十二站:命名管道

文章目录 一、命名管道介绍二、编码1.mkfifo2.unlink3.一个简单的例子4.修改 一、命名管道介绍 管道应用的一个限制就是只能在具有共同祖先&#xff08;具有亲缘关系&#xff09;的进程间通信。 如果我们想在不相关的进程之间交换数据&#xff0c;可以使用FIFO文件来做这项工作…

opencv009 滤波器01(卷积)

图像卷积操作&#xff08;convolution&#xff09;&#xff0c;或称为核操作&#xff08;kernel&#xff09;&#xff0c;是进行图像处理的一种常用手段&#xff0c; 图像卷积操作的目的是利用像素点和其邻域像素之前的空间关系&#xff0c;通过加权求和的操作&#xff0c;实现…

【服务器】Xshell与Xftp软件的使用指南

目录 【Xshell软件】 1.1 Xshell软件的功能 1.2 Xshell软件的使用 【Xftp软件】 2.1 Xftp软件的功能 2.2 Xftp软件的使用 可替代产品【FinalShell】 3.1 FinalShell软件的使用 3.2 FinalShell连接服务器失败解决方法 可替代产品【FileZilla】

VSCode无法下载插件,提示 Error while fetching extensions : XHR failed

解决方案&#xff1a; 打开vscode&#xff0c;依次点击File->Preferences->settings&#xff0c;中文就是文件->首选项->设置&#xff0c;打开如下图&#xff1a; 我们去搜索&#xff1a;Proxy &#xff0c; 然后回车 最重要的一步&#xff1a;将Http Prox…

关于 open ai,你了解多少?

OpenAI 的历史 第一阶段&#xff1a;2015-2018 年 2015 年&#xff0c;埃隆马斯克、山姆阿尔特曼、彼得蒂尔和杰西卡利文斯顿联合创立了 OpenAI。OpenAI 的目标是开发安全、有益的 AGI&#xff0c;并确保其造福全人类。在这一阶段&#xff0c;OpenAI 主要专注于基础研究&…

微信公众号怎么申请超过2个

一般可以申请多少个公众号&#xff1f;目前公众号申请数量的规定是从2018年底开始实施的&#xff0c;至今没有变化。规定如下&#xff1a;1、个人可以申请1个个人主体的公众号&#xff1b;2、企业&#xff08;有限公司&#xff09;可以申请2个公众号&#xff1b;3、个体户可以申…

中仕教育:2024年国考面试题型有哪些?

国考面试是选拔公务员的重要环节&#xff0c;通常涉及以下几个方面&#xff1a; 1. 自我介绍&#xff1a;考官通常会要求考生做一个简短的自我介绍。内容可以包括自己的基本情况、学历、工作经历等。 2. 专业知识&#xff1a;根据应聘者所应聘的职位和部门的不同&#xff0c;…

猛玛LARK M1无线麦克风采用 思远半导体 其实就是蓝牙话筒

自上世纪无线电技术开始发展起来&#xff0c;到了几十年后的今天&#xff0c;无线通讯技术已经成熟&#xff0c;开始追求更好的音质以及用户使用体验&#xff0c;优秀的产品也如雨后春笋般的出现&#xff0c;技术革新&#xff0c;极致音质&#xff0c;竞争也越来越激烈。这时候…

VC++中使用OpenCV进行人脸检测

VC中使用OpenCV进行人脸检测 对于上面的图像&#xff0c;如何使用OpenCV进行人脸检测呢&#xff1f; 使用OpenCV进行人脸检测十分简单&#xff0c;OpenCV官网给了一个Python人脸检测的示例程序&#xff0c; objectDetection.py代码如下&#xff1a; from __future__ import p…

【架构师视角系列】Apollo配置中心之Client端(二)

原创文章&#xff0c;转载请标注。https://blog.csdn.net/leeboyce/article/details/135733075 文章目录 声明配置中心系列文章一、客户端架构1、Config Service职责&#xff08;1&#xff09;配置管理&#xff08;2&#xff09;配置发布&#xff08;3&#xff09;配置读取 2、…