我的编程空间,编程开发者的网络收藏夹
学习永远不晚

MyBatis拦截器的实现原理

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

MyBatis拦截器的实现原理

前言

Mybatis拦截器并不是每个对象里面的方法都可以被拦截的。Mybatis拦截器只能拦截Executor、StatementHandler、ParameterHandler、ResultSetHandler四个类里面的方法,这四个对象在创建的时候才会创建代理。

用途:实际工作中,可以使用Mybatis拦截器来做一些SQL权限校验、数据过滤、数据加密脱敏、SQL执行时间性能监控和告警等。

 1.使用方法

以在Spring中创建 StatementHandler.update()方法的拦截器为例:

@Component
@Order(1)
@Intercepts({@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),})
public class SqlValidateMybatisInterceptor extends PRSMybatisInterceptor {
 
    @Override
    protected Object before(Invocation invocation) throws Throwable {
        String sql="";
        Statement statement=(Statement) invocation.getArgs()[0];
        if(Proxy.isProxyClass(statement.getClass())){
            MetaObject metaObject= SystemMetaObject.forObject(statement);
            Object h=metaObject.getValue("h");
            if(h instanceof StatementLogger){
                RoutingStatementHandler rsh=(RoutingStatementHandler) invocation.getTarget();
                sql=rsh.getBoundSql().getSql();
            }else {
                PreparedStatementLogger psl=(PreparedStatementLogger) h;
                sql=psl.getPreparedStatement().toString();
            }
        }else{
            sql=statement.toString();
        }
        if(containsDelete(sql)&&!containsWhere(sql)){
            throw new SQLException("不能删除整张表,sql:"+sql);
        }
        return null;
    }
 
    private boolean containsDelete(String sql){
        return sql.contains("delete")||sql.contains("DELETE");
    }
 
    private boolean containsWhere(String sql){
        return sql.contains("where")||sql.contains("WHERE");
    }
}
public class PRSMybatisInterceptor implements Interceptor {
 
    Boolean needBreak=false;
 
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result= before(invocation);
        if(needBreak){
            return result;
        }
        result= invocation.proceed();
        result=after(result,invocation);
        return result;
    }
 
    protected Object before(Invocation invocation) throws Throwable{
        return null;
    }
    protected Object after(Object result,Invocation invocation) throws Throwable{
        return result;
    }
    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }
    @Override
    public void setProperties(Properties properties) {
    }
}

1. 自定义拦截器 实现 org.apache.ibatis.plugin.Interceptor 接口与其中的方法。在plugin方法中需要返回 return Plugin.wrap(o, this)。在intercept方法中可以实现拦截的业务逻辑,改方法的 参数 Invocation中有原始调用的 对象,方法和参数,可以对其任意处理。

2. 在自定义的拦截器上添加需要拦截的对象和方法,通过注解 org.apache.ibatis.plugin.Intercepts 添加。如示例代码所示:

Intercepts的值是一个签名数组,签名中包含要拦截的 类,方法和参数。

2.MyBatis对象的创建

代理对象指的是:可以被拦截的4个类的实例。

代理对象创建时需要解析拦截器,从而利用JDK动态代理将拦截器的逻辑织入原始对象。

DefaultSqlSession中依赖Executor,如果新建的时候会创建executor

private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
    ...
    final Executor executor = configuration.newExecutor(tx, execType);
    return new DefaultSqlSession(configuration, executor, autoCommit);
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  if (ExecutorType.BATCH == executorType) {
    executor = new BatchExecutor(this, transaction);
  } else if (ExecutorType.REUSE == executorType) {
    executor = new ReuseExecutor(this, transaction);
  } else {
    executor = new SimpleExecutor(this, transaction);
  }
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

Executor中要用StatementHandler执行sql语句,StatementHandler是调用configuration.newStatementHandler()方法创建的。

StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
 
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}

StatementHandler依赖 parameterHandler 和 resultSetHandler,在构造 StatementHandler 时会调用一下方法创建这两个 handler。

this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
  parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
  return parameterHandler;
}
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
    ResultHandler resultHandler, BoundSql boundSql) {
  ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
  resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
  return resultSetHandler;
}

3.代理对象的创建

3.1 拦截器的获取

从对象的创建过程中可以看出 代理 对象的创建时通过 InterceptorChain.pluginAll() 方法创建的。

查看 拦截器链 InterceptorChain 发现,其中的拦截器的添加是在 Configuration 中。因为拦截器被声明为Bean了,所以在MyBatis初始化的时候,会扫描所有拦截器,添加到 InterceptorChain 中。

3.2 代理对象的创建

从上一步得知代理对象的创建是调用 Interceptor.pugin() 方法,然后调用 Plugin.wrap() 方法

Interceptor
@Override
public Object plugin(Object o) {
    return Plugin.wrap(o, this);
}

Plugin实现了 InvocationHandler 接口

 在 Plugin.wrap() 方法中会获取当前拦截器的接口,生成动态代理。

4. 拦截器的执行过程

在动态代理中当代理对象调用方法时,会将方法的调用委托给 InvocationHandler,也就是 Plugin,

如下图所示;

 在该方法中 获取拦截器签名中的方法,如果包含当前方法,则调用拦截方法,否则执行原方法的调用。

5. 拦截器的执行顺序

拦截器的顺序配置使用 Spring 中的 org.springframework.core.annotation.Order 注解配置。

order值大的拦截器先执行,order值大的在interceptors中越靠后,最后生成代理,所以先执行。

到此这篇关于MyBatis拦截器的实现原理的文章就介绍到这了,更多相关MyBatis拦截器 内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

MyBatis拦截器的实现原理

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

【mybatis】mybatis 拦截器工作原理源码解析

mybatis 拦截器工作原理(JDK动态代理)1. mybatis 拦截器案例场景:分页查询,类似成熟产品:pagehelper, 这里只做简单原理演示1.0 mybatis全局配置 SqlMapConfig.xml
【mybatis】mybatis 拦截器工作原理源码解析
2015-01-18

Struts2拦截器的实现原理是什么

Struts2拦截器的实现原理是什么?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。Struts2的核心在于它复杂的拦截器,几乎70%的工作都是由拦截器完成的。比
2023-05-31

Mybatis-Plus实现SQL拦截器的示例

这篇文章主要介绍了Mybatis-Plus实现一个SQL拦截器,通过使用SQL拦截器,开发人员可以在执行SQL语句之前或之后对其进行修改或记录,从而更好地控制和优化数据库操作,对Mybatis-Plus SQL拦截器相关知识感兴趣的朋友一起看看吧
2023-05-19

Java拦截器Interceptor实现原理是什么

这篇文章主要介绍“Java拦截器Interceptor实现原理是什么”,在日常操作中,相信很多人在Java拦截器Interceptor实现原理是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java拦截器
2023-06-22

Mybatis拦截器实现自定义需求

本文主要介绍了Mybatis拦截器实现自定义需求,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-19

MyBatis拦截器实现分页功能实例

由于业务关系 巴拉巴拉巴拉好吧 简单来说就是原来的业务是 需要再实现类里写 selectCount 和selectPage两个方法才能实现分页功能现在想要达到效果是 只通过一个方法就可以实现 也就是功能合并 所以就有了下面的实践既然是基于M
2023-05-31

SpringBoot拦截器实现登录拦截

SpringBoot拦截器可以做什么可以对URL路径进行拦截,可以用于权限验证、解决乱码、操作日志记录、性能监控、异常处理等。SpringBoot拦截器实现登录拦截pom.xml: 4.0.0 org.springframework
SpringBoot拦截器实现登录拦截
2015-07-20

利用Mybatis Plus实现一个SQL拦截器

SQL拦截器是一种用于拦截和修改Mybatis执行的SQL语句的工具,通过使用SQL拦截器,开发人员可以在执行SQL语句之前或之后对其进行修改或记录,本文就来借助一下Mybatis-Plus实现一个SQL拦截器吧
2023-05-19

mybatis实战之拦截器解读

目录myBATis实战之拦截器1、使用方法2、需要注意的地方拦截器的执行顺序与常用插件的整合遇到的问题可以提升的点总结mybatis实战之拦截器在服务的开发过程中,往往存在这样的需求,针对业务,实现对数据库操作语句做统一的处理。比如对某
2023-03-20

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

这篇文章将为大家详细讲解有关Springboot如何实现自定义mybatis拦截器,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。实践的准备 : 整合mybatis ,然后故意写了3个查询方法, 1个是li
2023-06-22

MyBatis中怎么实现自定义的SQL拦截器

在 MyBatis 中实现自定义的 SQL 拦截器,通常可以通过实现 org.apache.ibatis.plugin.Interceptor 接口来实现。下面是一个简单的示例:创建一个自定义的拦截器类,实现 Interceptor 接口:
MyBatis中怎么实现自定义的SQL拦截器
2024-05-08

MyBatis Plus 拦截器实现数据权限控制

一、介绍 上篇文章介绍的MyBatis Plus 插件实际上就是用拦截器实现的,MyBatis Plus拦截器对MyBatis的拦截器进行了包装处理,操作起来更加方便 二、自定义拦截器 2.1、InnerInterceptor MyBati
2023-08-20

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录