背景

关于Mybatis插件,大部分人都知道,也都使用过,但很多时候,我们仅仅是停留在表面上,知道Mybatis插件可以在DAO层进行拦截,如打印执行的SQL语句日志,做一些权限控制,分页等功能;但对其内部实现机制,涉及的软件设计模式,编程思想往往没有深入的理解。

本篇案例将帮助读者对Mybatis插件的使用场景,实现机制,以及其中涉及的编程思想进行一个小结,希望对以后的编程开发工作有所帮助。

注:本案例以mybatis 3.4.7-SNAPSHOT版本为例。

PS:文章是挺久之前写的,当时花了一些心思,存到电脑的word里,今天正好看到,就是里面的源码都是图片,哈哈哈,凑合着看吧。

Mybatis插件典型适用场景

分页功能

mybatis的分页默认是基于内存分页的(查出所有,再截取),数据量大的情况下效率较低,不过使用mybatis插件可以改变该行为,只需要拦截StatementHandler类的prepare方法,改变要执行的SQL语句为分页语句即可;

公共字段统一赋值

一般业务系统都会有创建者,创建时间,修改者,修改时间四个字段,对于这四个字段的赋值,实际上可以在DAO层统一拦截处理,可以用mybatis插件拦截Executor类的update方法,对相关参数进行统一赋值即可;

性能监控

对于SQL语句执行的性能监控,可以通过拦截Executor类的update, query等方法,用日志记录每个方法执行的时间;

其它

其实mybatis扩展性还是很强的,基于插件机制,基本上可以控制SQL执行的各个阶段,如执行阶段,参数处理阶段,语法构建阶段,结果集处理阶段,具体可以根据项目业务来实现对应业务逻辑。

Mybatis插件介绍

什么是Mybatis插件

与其称为Mybatis插件,不如叫Mybatis拦截器,更加符合其功能定位,实际上它就是一个拦截器,应用代理模式,在方法级别上进行拦截。

支持拦截的方法

  • 执行器Executor(update、query、commit、rollback等方法);
  • 参数处理器ParameterHandler(getParameterObject、setParameters方法);
  • 结果集处理器ResultSetHandler(handleResultSets、handleOutputParameters等方法);
  • SQL语法构建器StatementHandler(prepare、parameterize、batch、update、query等方法);

拦截阶段

那么这些类上的方法都是在什么阶段被拦截的呢?为理解这个问题,我们先看段简单的代码(摘自mybatis源码中的单元测试SqlSessionTest类),来了解下典型的mybatis执行流程,如下代码所示:

以上代码主要完成以下功能:

  • 读取mybatis的xml配置文件信息
  • 通过SqlSessionFactoryBuilder创建SqlSessionFactory对象
  • 通过SqlSessionFactory获取SqlSession对象
  • 执行SqlSession对象的selectList方法,查询结果
  • 关闭SqlSession

如下是时序图,在整个时序图中,涉及到mybatis插件部分已标红,基本上就是体现在上文中提到的四个类上,对这些类上的方法进行拦截。

Mybatis插件实现机制

插件配置信息的加载

先来看下mybatis是如何加载插件配置的,对应的xml配置信息如下:

对应的解析代码如下,主要做以下工作:

  1. 根据解析到的类信息创建Interceptor对象;
  2. 调用setProperties方法设置属性变量;
  3. 添加到Configuration的interceptorChain拦截器链中;

以上逻辑对应的时序图如下:

代理对象的生成

Mybatis插件的实现机制主要是基于动态代理实现的,其中最为关键的就是代理对象的生成,所以有必要来了解下这些代理对象是如何生成的。

Executor代理对象

ParameterHandler代理对象

ResultSetHandler代理对象

StatementHandler代理对象

观察源码,发现这些可拦截的类对应的对象生成都是通过InterceptorChain的pluginAll方法来创建的,进一步观察pluginAll方法,如下:

遍历所有拦截器,调用拦截器的plugin方法生成代理对象,注意生成代理对象重新赋值给target,所以如果有多个拦截器的话,生成的代理对象会被另一个代理对象代理,从而形成一个代理链条,执行的时候,依次执行所有拦截器的拦截逻辑代码;

接下来看一下我们在编写拦截器的时候,一个典型的plugin方法实现方式,如下:

再进一步查看wrap方法,如下:

典型的动态代理实现,调用的是Proxy.newProxyInstance方法来生成代理对象。

以上逻辑对应的时序图如下,这里我们假设声明了两个拦截器,那么在创建target代理对象的时候,最终返回的代理对象proxy2,实际上代理了proxy1,而proxy1又代理了target,:

拦截逻辑的执行

由于真正去执行Executor、ParameterHandler、ResultSetHandler和StatementHandler类中的方法的对象是代理对象(建议将代理对象转为class文件,反编译查看其结构,帮助理解),所以在执行方法时,首先调用的是Plugin类(实现了InvocationHandler接口)的invoke方法,如下:

首先根据执行方法所属类获取拦截器中声明需要拦截的方法集合;

判断当前方法需不需要执行拦截逻辑,需要的话,执行拦截逻辑方法(即Interceptor接口的intercept方法实现),不需要则直接执行原方法。

可以关注下Interceptor接口的intercept方法实现,一般需要用户自定义实现逻辑,其中有一个重要参数,即Invocation类,通过改参数我们可以获取执行对象,执行方法,以及执行方法上的参数,从而进行各种业务逻辑实现,一般在该方法的最后一句代码都是invocation.proceed()(内部执行method.invoke方法),否则将无法执行下一个拦截器的intercept方法。

以上逻辑对应的时序图如下,这里我们以执行executor对象的query方法为例,且假设有两个拦截器存在:

Mybatis插件开发例子

这里以分页插件为例,来了解下一般mybatis插件的编写规则,如下所示:

主要需要实现三个方法

  1. intercept:在此实现自己的拦截逻辑,可从Invocation参数中拿到执行方法的对象,方法,方法参数,从而实现各种业务逻辑, 如下代码所示,从invocation中获取的statementHandler对象即为被代理对象,基于该对象,我们获取到了执行的原始SQL语句,以及prepare方法上的分页参数,并更改SQL语句为新的分页语句,最后调用invocation.proceed()返回结果。
  2. plugin:生成代理对象;
  3. setProperties:设置一些属性变量;

小结

简单的说,mybatis插件就是对ParameterHandler、ResultSetHandler、StatementHandler、Executor这四个接口上的方法进行拦截,利用JDK动态代理机制,为这些接口的实现类创建代理对象,在执行方法时,先去执行代理对象的方法,从而执行自己编写的拦截逻辑,所以真正要用好mybatis插件,主要还是要熟悉这四个接口的方法以及这些方法上的参数的含义;

另外,如果配置了多个拦截器的话,会出现层层代理的情况,即代理对象代理了另外一个代理对象,形成一个代理链条,执行的时候,也是层层执行;

关于mybatis插件涉及到的设计模式和软件思想如下:

  1. 设计模式:代理模式、责任链模式;
  2. 软件思想:AOP编程思想,降低模块间的耦合度,使业务模块更加独立;

一些注意事项:

  1. 不要定义过多的插件,代理嵌套过多,执行方法的时候,比较耗性能;
  2. 拦截器实现类的intercept方法里最后不要忘了执行invocation.proceed()方法,否则多个拦截器情况下,执行链条会断掉;

深入理解Mybatis插件开发的更多相关文章

  1. 《深入理解mybatis原理》 MyBatis事务管理机制

    MyBatis作为Java语言的数据库框架,对数据库的事务管理是其很重要的一个方面.本文将讲述MyBatis的事务管理的实现机制. 首先介绍MyBatis的事务Transaction的接口设计以及其不 ...

  2. 《深入理解mybatis原理》 Mybatis初始化机制具体解释

    对于不论什么框架而言.在使用前都要进行一系列的初始化,MyBatis也不例外. 本章将通过下面几点具体介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XM ...

  3. 《深入理解mybatis原理》 MyBatis的架构设计以及实例分析

    作者博客:http://blog.csdn.net/u010349169/article/category/2309433 MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简 ...

  4. 深入理解mybatis原理, Mybatis初始化SqlSessionFactory机制详解(转)

    文章转自http://blog.csdn.net/l454822901/article/details/51829785 对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章 ...

  5. Mybatis插件开发

    前面几篇文章介绍了Mybtis中四个重要的对象,其中提到它们都是在Configuration中被创建的,我们一起看一下创建四大对象的方法,代码如下所示: public ParameterHandler ...

  6. 深入理解mybatis

    MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简单.优雅.本文主要讲述MyBatis的架构设计思路,并且讨论MyBatis的几个核心部件,然后结合一个select查询实例, ...

  7. 《深入理解mybatis原理7》 MyBatis的二级缓存的设计原理

    <深入理解mybatis原理> MyBatis的二级缓存的设计原理 MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能.本文将全面分 ...

  8. 《深入理解mybatis原理6》 MyBatis的一级缓存实现详解 及使用注意事项

    <深入理解mybatis原理> MyBatis的一级缓存实现详解 及使用注意事项 0.写在前面   MyBatis是一个简单,小巧但功能非常强大的ORM开源框架,它的功能强大也体现在它的缓 ...

  9. 《深入理解mybatis原理4》 MyBatis缓存机制的设计与实现

    <深入理解mybatis原理> MyBatis缓存机制的设计与实现 本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存 ...

随机推荐

  1. 用Sklearn画一颗决策树

    小伙伴们大家好~o( ̄▽ ̄)ブ,首先声明一下,我的开发环境是Jupyter lab,所用的库和版本大家参考: Python 3.7.1(你的版本至少要3.4以上 Scikit-learn 0.20.0 ...

  2. 《Java项目中classpath路径详解》

    项目里用到了classpath路径来引用文件,那么classpath指的是哪里呢 我首先把上面的applicationContext.xml文件放在了src目录下发现可以. 那么classpath到底 ...

  3. 记录新项目中遇到的技术及自己忘记的技术点【DES加密解密,MD5加密,字符串压缩、解压,字符串截取等操作】

    一.DES加密.解密 #region DES加密解密 /// <summary> /// 进行DES加密 /// </summary> /// <param name=& ...

  4. 页面跳转之session

    题意:设主页为index.jsp 通过session将index.jsp中的信息传送给结果页面result.jsp.(其实,session的生命周期是整个服务器开启过程,具体不做详细介绍).这个 其实 ...

  5. 从Excel导数据到MySQL速度优化

    运行环境: Windows10 和 Deepin15.7, MySQL14.4, Java1.8.0_181使用工具: poi,JDBC数据规模: 35万条,5个文件夹,146个Excel文件(.xl ...

  6. Shell中sed使用

    sed是一种在线编辑器,它一次处理一行内容.处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往 ...

  7. 通过命令行操作MYSQL的方法 以及导入大的SQL备份文件

    运行  输入CMD 进入 命令行窗口 输入Mysql.exe的路径  如:c:/wamp/bin/mysql.exe  回车 这时出现 welcome to the mysql ...的提示  进入成 ...

  8. LeetCode 15 3Sum [sort] <c++>

    LeetCode 15 3Sum [sort] <c++> 给出一个一维数组,找出其中所有和为零的三元组(元素集相同的视作同一个三元组)的集合. C++ 先自己写了一发,虽然过了,但跑了3 ...

  9. DP-01背包

    题目传送门 题目类似01背包,但存在一个选取先后不同价值会有损耗,所有对物品按易损耗的程度从大到小排个序来顺序选取. #include<bits/stdc++.h> using names ...

  10. JPG、PNG、GIF、SVG 等格式图片区别

    1.图片 2. 前言 首先,我们要清楚的是,图片从类型上分,可以分为 位图 和 矢量图. 位图:位图又叫点阵图或像素图,计算机屏幕上的图是由屏幕上的发光点(即像素)构成的,每个点用二进制数据来描述其颜 ...