千家信息网

Springboot如何实现自定义mybatis拦截器

发表于:2025-11-10 作者:千家信息网编辑
千家信息网最后更新 2025年11月10日,这篇文章将为大家详细讲解有关Springboot如何实现自定义mybatis拦截器,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。实践的准备 :整合mybatis ,
千家信息网最后更新 2025年11月10日Springboot如何实现自定义mybatis拦截器

这篇文章将为大家详细讲解有关Springboot如何实现自定义mybatis拦截器,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

实践的准备 :

整合mybatis ,然后故意写了3个查询方法, 1个是list 列表数据,2个是 单条数据 。

我们通过自己写一个MybatisInterceptor实现 mybatis框架的 Interceptor来做文章:

MybatisInterceptor.java :

import org.apache.ibatis.executor.Executor;import org.apache.ibatis.mapping.*;import org.apache.ibatis.plugin.*;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import org.springframework.stereotype.Component; import java.lang.reflect.Method;import java.util.*;   /** * @Author JCccc * @Description * @Date 2021/12/14 16:56 */@Component@Intercepts({        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})public class MybatisInterceptor implements Interceptor {     @Override    public Object intercept(Invocation invocation) throws Throwable {        //获取执行参数        Object[] objects = invocation.getArgs();        MappedStatement ms = (MappedStatement) objects[0];        //解析执行sql的map方法,开始自定义规则匹配逻辑        String mapperMethodAllName = ms.getId();        int lastIndex = mapperMethodAllName.lastIndexOf(".");        String mapperClassStr = mapperMethodAllName.substring(0, lastIndex);        String mapperClassMethodStr = mapperMethodAllName.substring((lastIndex + 1));        Class mapperClass = Class.forName(mapperClassStr);        Method[] methods = mapperClass.getMethods();        Class returnType;        for (Method method : methods) {            if (method.getName().equals(mapperClassMethodStr)) {                returnType = method.getReturnType();                if (returnType.isAssignableFrom(List.class)) {                    System.out.println("返回类型是 List");                    System.out.println("针对List 做一些操作");                } else if (returnType.isAssignableFrom(Set.class)) {                    System.out.println("返回类型是 Set");                    System.out.println("针对Set 做一些操作");                } else{                    BoundSql boundSql = ms.getSqlSource().getBoundSql(objects[1]);                    String oldSql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]", " ");                    if (!oldSql.contains("LIMIT")){                        String newSql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]", " ") + " LIMIT 1";                        BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), newSql,                                boundSql.getParameterMappings(), boundSql.getParameterObject());                        MappedStatement newMs = newMappedStatement(ms, new MyBoundSqlSqlSource(newBoundSql));                        for (ParameterMapping mapping : boundSql.getParameterMappings()) {                            String prop = mapping.getProperty();                            if (boundSql.hasAdditionalParameter(prop)) {                                newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));                            }                        }                        Object[] queryArgs = invocation.getArgs();                        queryArgs[0] = newMs;                        System.out.println("打印新SQL语句" + newSql);                    }                 }            }        }        //继续执行逻辑        return invocation.proceed();    }      @Override    public Object plugin(Object o) {        //获取代理权        if (o instanceof Executor) {            //如果是Executor(执行增删改查操作),则拦截下来            return Plugin.wrap(o, this);        } else {            return o;        }    }     /**     * 定义一个内部辅助类,作用是包装 SQL     */    class MyBoundSqlSqlSource implements SqlSource {        private BoundSql boundSql;         public MyBoundSqlSqlSource(BoundSql boundSql) {            this.boundSql = boundSql;        }         @Override        public BoundSql getBoundSql(Object parameterObject) {            return boundSql;        }     }     private MappedStatement newMappedStatement(MappedStatement ms, SqlSource newSqlSource) {        MappedStatement.Builder builder = new                MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());        builder.resource(ms.getResource());        builder.fetchSize(ms.getFetchSize());        builder.statementType(ms.getStatementType());        builder.keyGenerator(ms.getKeyGenerator());        if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0) {            builder.keyProperty(ms.getKeyProperties()[0]);        }        builder.timeout(ms.getTimeout());        builder.parameterMap(ms.getParameterMap());        builder.resultMaps(ms.getResultMaps());        builder.resultSetType(ms.getResultSetType());        builder.cache(ms.getCache());        builder.flushCacheRequired(ms.isFlushCacheRequired());        builder.useCache(ms.isUseCache());        return builder.build();    }      @Override    public void setProperties(Properties properties) {        //读取mybatis配置文件中属性    }  }

简单代码端解析:

① 拿出执行参数,里面有很多东西给我们用的,我本篇主要是用MappedStatement。大家可以发挥自己的想法。 打debug可以自己看看里面的东西。

//获取执行参数Object[] objects = invocation.getArgs();MappedStatement ms = (MappedStatement) objects[0];

② 这里我主要是使用了从MappedStatement里面拿出来的id,做切割分别切出来 目前执行的sql的mapper是哪个,然后方法是哪个。

String mapperMethodAllName = ms.getId();int lastIndex = mapperMethodAllName.lastIndexOf(".");String mapperClassStr = mapperMethodAllName.substring(0, lastIndex);String mapperClassMethodStr = mapperMethodAllName.substring((lastIndex + 1));

③ 这就是我自己随便写的一下,我直接根据切割出来的mapper类找出里面的方法,主要是为了拿出每个方法的返回类型,因为 我这篇实践内容就是,判断出单个pojo接受的mapper方法,加个LIMIT 1 , 其他的 LIST 、SET 、 Page 等等这些,我暂时不做扩展。

Class mapperClass = Class.forName(mapperClassStr);Method[] methods = mapperClass.getMethods();Class returnType;

④这一段代码就是该篇的拓展逻辑了,从MappedStatement里面拿出 SqlSource,然后再拿出BoundSql,然后一顿操作 给加上 LIMIT 1 , 然后new 一个新的 MappedStatement,一顿操作塞回去invocation 里面

BoundSql boundSql = ms.getSqlSource().getBoundSql(objects[1]);String oldSql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]", " ");if (!oldSql.contains("LIMIT")){    String newSql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]", " ") + " LIMIT 1";    BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), newSql,            boundSql.getParameterMappings(), boundSql.getParameterObject());    MappedStatement newMs = newMappedStatement(ms, new MyBoundSqlSqlSource(newBoundSql));    for (ParameterMapping mapping : boundSql.getParameterMappings()) {        String prop = mapping.getProperty();        if (boundSql.hasAdditionalParameter(prop)) {            newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));        }    }    Object[] queryArgs = invocation.getArgs();    queryArgs[0] = newMs;    System.out.println("打印新SQL语句:" + newSql);}

OK,最后简单来试试效果 :

最后再重申一下,本篇文章内容只是一个抛砖引玉,随便想的一些实践效果,大家可以按照自己的想法发挥。

最后再补充一个 解决 mybatis自定义拦截器和 pagehelper 拦截器 冲突导致失效的问题出现的

解决方案:

加上一个拦截器配置类

MyDataSourceInterceptorConfig.java :

import org.apache.ibatis.session.SqlSessionFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.ApplicationListener;import org.springframework.context.event.ContextRefreshedEvent;import org.springframework.stereotype.Component; import java.util.List; /** * @Author JCccc * @Description * @Date 2021/12/14 16:56 */@Componentpublic class MyDataSourceInterceptorConfig implements ApplicationListener {     @Autowired    private MybatisInterceptor mybatisInterceptor;     @Autowired    private List sqlSessionFactories;     @Override    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {        for (SqlSessionFactory factory : sqlSessionFactories) {            factory.getConfiguration().addInterceptor(mybatisInterceptor);        }    }}

最后最后再补充多一些,

文中 针对拦截的是Executor 这种接口的插件,

其实 还可以使用ParameterHandler、ResultSetHandler、StatementHandler。

每当执行这四种接口对象的方法时,就会进入拦截方法,然后我们可以根据不同的插件去拿不同的参数。

类似:

@Signature(method = "prepare", type = StatementHandler.class, args = {Connection.class,Integer.class})

然后可以做个转换,也是可以取出相关的 BoundSql 等等 :

if (!(invocation.getTarget() instanceof RoutingStatementHandler)){                return invocation.proceed();            }            RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation.getTarget();            BoundSql boundSql = statementHandler.getBoundSql();            String sql = boundSql.getSql().toUpperCase();

ParameterHandler、ResultSetHandler、StatementHandler、Executor ,不同的插件拦截,有不同的使用场景,想深入的看客们,可以深一下。

关于"Springboot如何实现自定义mybatis拦截器"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

方法 拦截器 不同 参数 内容 就是 插件 篇文章 类型 逻辑 实践 东西 代码 想法 接口 效果 数据 文章 更多 语句 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 台州启商网络技术有限公司 唯一深圳互联网科技有限公司 服务器装二个版本sql 虹口区软件开发项目 国产化服务器与安防视频监控 家用网络安全知识手抄报 畅捷通数据库怎么设置 梦幻西游手游流沙河服务器 预防中小学生网络安全知识 手机简单安卓软件开发 四个实体的数据库模型有哪些 非肿瘤数据库GEO 天津软件开发企业纳税比例 公司网站服务器管理 网络安全博弈论教材 数据库层面的乐观锁和悲观锁 泰拉瑞亚1.4服务器ip怎么看 沂蒙雪山服务器能往外转区吗 二年级上册网络安全画 服务器安全设置在哪 网络安全与普通人的关系 服务器怎么快速转移数据 最大的互联网科技 手机简单安卓软件开发 天津服务器回收价目表虚拟主机 什么叫网络安全任务 青岛计算机网络技术招聘 厦门住宿软件开发 国家网络安全宣传周宣传通稿 新吴区机电软件开发注意事项
0