MyBatis拦截器的实现原理

目录
  • 前言
  • 1.使用方法
  • 2.MyBatis对象的创建
  • 3.代理对象的创建
    • 3.1 拦截器的获取
    • 3.2 代理对象的创建
  • 4. 拦截器的执行过程
  • 5. 拦截器的执行顺序

前言

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拦截器 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • spring boot Mybatis 拦截器实现拼接sql和修改的代码详解

    定义一个 SqlIntercepor 类 import com.culturalCenter.placeManage.globalConfig.Interface.InterceptAnnotation; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedSta

  • mybatis/mybatis-plus模糊查询语句特殊字符转义拦截器的实现

    目录 1.使用mybatis提供的拦截器拦截所有的查询请求. 2.定义SQL语句转义模板,分别对Map和Object对象进行处理 mybatis/mybatis-plus模糊查询语句特殊字符转义拦截器 在开发中,我们通常会遇到这样的情况.用户在录入信息是录入了'%',而在查询时无法精确匹配'%'.究其原因,'%'是MySQL的关键字,如果我们想要精确匹配'%',那么需要对其进行转义. 1.使用mybatis提供的拦截器拦截所有的查询请求. 具体实现在代码中均有注释 import lombok.e

  • MyBatis拦截器动态替换表名的方法详解

    目录 写在前面 一.Mybatis Interceptor 拦截器接口和注解 二.实现思路 三.代码实现 四.运行结果 写在最后 参考资料 写在前面 今天收到一个需求,根据请求方的不同,动态的切换表名(涵盖SELECT,INSERT,UPDATE操作).几张新表和旧表的结构完全一致,但是分开维护.看到需求第一反应是将表名提出来当${tableName}参数,然后AOP拦截判断再替换表名.但是后面看了一下这几张表在很多mapper接口都有使用,其中还有一些复杂的连接查询,提取tableName当参

  • Mybatis拦截器实现数据权限的示例代码

    在我们日常开发过程中,通常会涉及到数据权限问题,下面以我们常见的一种场景举例: 一个公司有很多部门,每个人所处的部门和角色也不同,所以数据权限也可能不同,比如超级管理员可以查看某张 表的素有信息,部门领导可以查看该部门下的相关信息,部门普通人员只可以查看个人相关信息,而且由于角色的 不同,各个角色所能查看到的数据库字段也可能不相同,那么此处就涉及到了数据权限相关的问题.那么我们该如 何处理数据权限相关的问题呢?我们提供一种通过Mybatis拦截器实现的方式,下面我们来具体实现一下 pom.xml

  • mybatis拦截器无法注入spring bean的问题解决

    公司要整合rabbitmq与mybatis拦截器做一个数据同步功能. 整合过程中大部分环节都没什么问题,就是遇到了mybatis拦截器 @Intercepts(@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})) @Component public class MyBatisInterceptor implements Interceptor

  • 详解Mybatis拦截器安全加解密MySQL数据实战

    需求背景 公司为了通过一些金融安全指标(政策问题)和防止数据泄漏,需要对用户敏感数据进行加密,所以在公司项目中所有存储了用户信息的数据库都需要进行数据加密改造.包括Mysql.redis.mongodb.es.HBase等. 因为在项目中是使用springboot+mybatis方式连接数据库进行增删改查,并且项目是中途改造数据.所以为了不影响正常业务,打算这次改动尽量不侵入到业务代码,加上mybatis开放的各种拦截器接口,所以就以此进行改造数据. 本篇文章讲述如何在现有项目中尽量不侵入业务方

  • MyBatis拦截器的实现原理

    目录 前言 1.使用方法 2.MyBatis对象的创建 3.代理对象的创建 3.1 拦截器的获取 3.2 代理对象的创建 4. 拦截器的执行过程 5. 拦截器的执行顺序 前言 Mybatis拦截器并不是每个对象里面的方法都可以被拦截的.Mybatis拦截器只能拦截Executor.StatementHandler.ParameterHandler.ResultSetHandler四个类里面的方法,这四个对象在创建的时候才会创建代理. 用途:实际工作中,可以使用Mybatis拦截器来做一些SQL权

  • MyBatis拦截器原理探究

    MyBatis拦截器介绍 MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能.那么拦截器拦截MyBatis中的哪些内容呢? 我们进入官网看一看: MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用.默认情况下,MyBatis 允许使用插件来拦截的方法调用包括: Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) P

  • 简单了解mybatis拦截器实现原理及实例

    这篇文章主要介绍了简单了解mybatis拦截器实现原理及实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 例行惯例,先看些基本概念: 1 拦截器的作用就是我们可以拦截某些方法的调用,在目标方法前后加上我们自己逻辑 2 Mybatis拦截器设计的一个初衷是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑. 自定义拦截器 /** * mybatis 自定义拦截器 * 三步骤: * 1 实现 {@link Intercept

  • MyBatis拦截器的原理与使用

    目录 二.拦截器注册的三种方式 1.XML注册 2.配置类注册 3.注解方式 三.ParameterHandler参数改写-修改时间和修改人统一插入 四.通过StatementHandler改写SQL 一.拦截对象和接口实现示例 MyBatis拦截器的作用是在于Dao到DB中间进行额外的处理.大部分情况下通过mybatis的xml配置sql都可以达到想要的DB操作效果,然而存在一些类似或者相同的查询条件或者查询要求,这些可以通过拦截器的实现可以提升开发效率,比如:分页.插入和更新时间/人.数据权

  • MyBatis拦截器实现分页功能的实现方法

    MyBatis拦截器实现分页功能的实现方法 前言: 首先说下实现原理.使用拦截器拦截原始的sql,然后加上分页查询的关键字和属性,拼装成新的sql语句再交给mybatis去执行. 除了业务代码之外,需要写的东西不多,提几个关键的: 1.分页对象Page类.给该对象设置一个当前页数(前端给).总记录数(拦截器内赋值)2个参数,他就能帮你计算出分页sql语句用的2个参数. /** * 分页对应的实体类 */ public class Page { /** * 总条数 */ private int t

  • java MyBatis拦截器Inteceptor详细介绍

    有许多java初学者对于MyBatis拦截器Inteceptor不是很了解,在这里我来为各位整理下篇关于java中MyBatis拦截器Inteceptor详解, 本文主要分析MyBatis的插件机制,实际就是Java动态代理实现的责任链模式实现. 根据官方文档.Mybatis只允许拦截以下方法,这个决定写拦截器注解签名参数. 代码如下 Executor (update, query, flushStatements, commit, rollback, getTransaction, close

  • MyBatis拦截器:给参数对象属性赋值的实例

    该拦截器的作用:在进行增加.修改等操作时,给数据模型的一些通用操作属性(如:创建人.创建时间.修改人.修改时间等)自动赋值. 该实现是在DAO层拦截,即存入DB前最后一层.后经分析,不是很合理,改为在service层拦截,用spring AOP来实现了,该代码遂弃用.不过已经测试可用,记录备忘. package com.development; import java.lang.reflect.InvocationTargetException; import java.util.Date; i

  • Mybatis拦截器的实现介绍

     MyBatis介绍 MyBatis本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis .它支持普通 SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索.MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的

  • Mybatis拦截器实现分页

    最终dao层结果: public interface ModelMapper { Page<Model> pageByConditions(RowBounds rowBounds, Model record); } 接下来一步一步来实现分页. 一.创建Page对象: public class Page<T> extends PageList<T> { private int pageNo = 1;// 页码,默认是第一页 private int pageSize = 1

  • Struts2拦截器Interceptor的原理与配置实例详解

    一.Struts2拦截器原理: Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts 2会查找配置文件,并根据其配置实例化相对的    拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器. 比如:应用要求用户登陆,且必须为指定用户名才可以查看系统中某个视图资源:否则,系统直接转入登陆页面.对于上面的需求,可以在每个Action的执行实际处理逻辑之前,先执行权限检查逻辑,但这种做法不利于代码复用.因为大部分Action里的权限检查代码都大同小异,故

随机推荐