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

这篇具有很好参考价值的文章主要介绍了自定义注解与拦截器实现不规范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);

        // 对类中所有满足条件的类增加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文件。
自定义注解与拦截器实现不规范sql拦截(自定义注解填充插件篇),java,idea插件,java,intellij-idea
自定义注解与拦截器实现不规范sql拦截(自定义注解填充插件篇),java,idea插件,java,intellij-idea
之后,在idea的settings中搜索plugins,点击配置中的本地install选项,即可选择并加载对应的插件jar。
自定义注解与拦截器实现不规范sql拦截(自定义注解填充插件篇),java,idea插件,java,intellij-idea

效果展示

创建一个简单的UserMapper类。

public interface UserMapper {

    public String queryG();

    public String queryKKP();
}

在编辑页面上右键显示菜单,点击我们之前设置的新按钮增加自定义注解信息,增加成功。
自定义注解与拦截器实现不规范sql拦截(自定义注解填充插件篇),java,idea插件,java,intellij-idea

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

总结

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

到了这里,关于自定义注解与拦截器实现不规范sql拦截(自定义注解填充插件篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 防重复提交:自定义注解 + 拦截器(HandlerInterceptor)

    防重复提交:自定义注解 + 拦截器(HandlerInterceptor) 一、思路: 1、首先自定义注解; 2、创建拦截器实现类(自定义类名称),拦截器(HandlerInterceptor); 3、创建类:配置拦截器路径(拦截URL规则); 二、代码示例: 1、首先自定义注解; 2、创建拦截器实现类(自定义类名

    2024年02月10日
    浏览(29)
  • Springboot 自定义 Mybatis拦截器,实现 动态查询条件SQL自动组装拼接(玩具)

    ps:最近在参与3100保卫战,战况很激烈,刚刚打完仗,来更新一下之前写了一半的博客。 该篇针对日常写查询的时候,那些动态条件sql 做个简单的封装,自动生成(抛砖引玉,搞个小玩具,不喜勿喷)。 来看看我们平时写那些查询,基本上都要写的一些动态sql:   一个字段

    2024年02月12日
    浏览(35)
  • SpringCloud微服务实战——搭建企业级开发框架:微服务安全加固—自定义Gateway拦截器实现防止SQL注入/XSS攻击

     SQL注入是常见的系统安全问题之一,用户通过特定方式向系统发送SQL脚本,可直接自定义操作系统数据库,如果系统没有对SQL注入进行拦截,那么用户甚至可以直接对数据库进行增删改查等操作。   XSS全称为Cross Site Script跨站点脚本攻击,和SQL注入类似,都是通过特定方

    2024年02月03日
    浏览(50)
  • 自定义拦截器实现

    在 Spring MVC 框架中, 拦截器作为一种机制, 用于对请求进行拦截. 拦截器可以在请求进入处理器之前、处理器返回处理之后、视图渲染之前等各个环节进行拦截. 拦截器通常用于实现一下功能 : 鉴权和身份认证 日志记录和统计 请求参数和校验和过滤 缓存和性能优化 路径重定向

    2024年02月09日
    浏览(37)
  • SpringBoot Redis 注解 拦截器来实现接口幂等性校验

    幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次 比如:订单接口, 不能多次创建订单 支付接口, 重复支付同一笔订单只能扣一次钱 支付宝回调接口, 可能会多次回调, 必须处理重复回调 普通表单提交接口, 因为网络超时等原因多次点击提

    2024年01月19日
    浏览(41)
  • Spring Boot入门(23):记录接口日志再也不难!用AOP和自定义注解给Spring Boot加上日志拦截器!

            在上两期中,我们着重介绍了如何集成使用 Logback 与 log4j2 日志框架的使用,今天我们讲解的主题依旧跟日志有关,不过不是使用何种开源框架,而是自己动手造。         Spring的核心之一AOP;AOP翻译过来叫面向切面编程, 核心就是这个切面. 切面表示从业务逻辑中

    2024年02月11日
    浏览(41)
  • MybatisPlusInterceptor实现sql拦截器(超详细)

    1 . 导入pom 2 . 配置下MybatisPlus的yml 3 . 实体类 4 .  DTO 5 . MybatisPlus的config 6 . controller 7 . 测试  成功实现sql拦截并进行拼接

    2024年02月11日
    浏览(42)
  • 利用Mybatis拦截器实现自定义的ID增长器

    原生的Mybatis框架是没有ID自增器,但例如国产的Mybatis Plus却是支持,不过,Mybatis Plus却是缺少了自定属性的填充;例如:我们需要自定义填充一些属性,updateDate、createDate等,这时Mybatis Plus自带的ID自增器就无法满足需求;这种时候我们就需要自定义的ID增加器,可以自定义

    2024年02月19日
    浏览(33)
  • Mybatis拦截器注解@Intercepts与@Signature注解属性说明

    可能有些新手使用mybatis拦截器的时候可能没太懂@Signature注解中type,method,args的用法 首先mybatis拦截器可以拦截如下4中类型 Executor sql的内部执行器 ParameterHandler 拦截参数的处理 StatementHandler 拦截sql的构建 ResultSetHandler 拦截结果的处理 type:就是指定拦截器类型(ParameterHandl

    2024年02月05日
    浏览(31)
  • 【SpringMVC】JSR 303与拦截器注解使用

       JSR 303,它是Java EE(现在称为Jakarta EE)规范中的一部分。JSR 303定义了一种用于验证Java对象的标准规范,也称为Bean验证。         Bean验证是一种用于验证对象属性的框架,它可以确保对象符合特定的规则和约束。这些规则可以包括字段的非空性、长度限制、格式验证等。

    2024年02月07日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包