《深入浅出MyBatis技术原理与实战》——7. 插件
在第6章讨论了四大运行对象的运行过程,在Configuration对象的创建方法里我们看到了MyBatis用责任链去封装它们。
7.1 插件接口
在MyBatis中使用插件,我们必须使用接口Interceptor,先来看看它的定义和各个方法的含义:


在接口各中,运用了3个方法,这3个方法的含义是:

这里看到了插件的骨架,这样的模式我们称为模版模式,就是提供一个骨架,并且告知骨架中的方法是干什么用的,由开发者来完成它。
7.2 插件的初始化
插件的初始化实在MyBatis初始化的时候完成的,这点可以通过XMLConfigBuilder中的代码知道:


在解析配置文件的时候,在MyBatis的上下文初始化过程中,就开始读入插件节点和我们配置的参数,同时使用反射技术生成对应的插件实例,然后调用插件方法中的setProperties方法,设置我们配置的参数,然后将插件实例保存到配置对象中,以便读取和使用它。所以插件的实例对象是一开始就被初始化的,而不是用到的时候才初始化的,我们使用它的时候,直接拿出来就可以了,这样有助于性能的提高。
再来看看插件在Configuration对象里是怎样保存的:

InterceptorChain在Configuration里面是一个属性,它里面有个addInterceptor方法:

显然,完成初始化的插件就保存在这个List对象里面等待将其取出使用。
7.3 插件的代理和反射设计
插件用的是责任链模式。就是一个对象,在MyBatis中可能是四大对象中的一个,在多个角色中传递,处在传递链上的任何角色都有处理它的机会。
MyBatis的责任链是由InterceptorChain去定义的,MyBatis在创建执行器时用到这样的代码:

来看看pluginAll()方法是如何实现的:

plugin方法是生成代理对象的方法,当它取出插件的时候是从Configuration对象中去取的。从第一个对象(四大对象中的一个)开始,将对象传递给了plugin方法,然后返回一个代理,如果存在第二个插件,那么就拿到第一个代理对象的代理,传递给plugin方法再返回第一个代理对象的代理......以此类推,有多少个拦截器就生成多少个代理对象。这样有多少个拦截器就生成多少个代理对象。
MyBatis中提供了一个常用的工具类来生成代理对象,它便是Plugin类。Plugin类实现了InvocationHandler接口,采用的是JDK动态代理,我们首先看看这个类的两个十分重要的方法:


看以看到它是一个动态代理对象,其中wrap方法为我们生成这个对象的动态代理对象。至于invoke方法,如果使用这个类为插件生成代理对象,那么代理对象在调用方法的时候就会进入到invoke方法中。在invoke方法中,如果存在签名的拦截方法,插件的intercept方法就会被我们在这里调用,然后就返回结果。如果不存在签名的方法,那么将直接调度我们要执行的方法。
我们创建一个Invocation对象,其构造方法的参数包括被代理对象、方法及其参数。Invocation对象进行初始化,它有一个proceed()方法:


这个方法就是调度被代理对象的真实方法。现在假设有n个插件,我们知道第一个传递的参数是四大对象本身,然后调用一次wrap方法产生第一个代理对象,而这里的反射就是反射四大对象本身的真实方法。如果有第二个插件,我们会将第一个代理对象传递给wrap方法,生成第二个代理对象,这里的反射就是指第一个代理对象的invoke方法,依次类推直至最后一个代理对象。如果每一个代理对象都调用这个proceed方法,那么最后四大对象本身的方法也会被调用,只是它会从最后一个代理对象的invoke方法运行到第一个代理对象的invoke方法,直至四大对象的真实方法。
7.4 常用的工具类——MetaObject
可以有效的读取或者修改一些重要的对象属性。它有3个方法常常用到:


我们可以通过使用它来给四大对象的某些属性赋值从而满足我们的需要。例如,拦截StatementHandler对象,我们需要先获取她要执行的SQL修改它的一些值。这个时候我们可以使用MetaObject,如:

拦截的StatementHandler实际上是RoutingStatementHandler对象,它的delegate属性才是真实服务的StatementHandler,真实的StatementHandler有一个属性BoundSql,它下面又有一个属性sql。所以才有了路径delegate.boundSql.sql。我们就可以通过这个路径获取或者修改对应运行时的SQL。
7.5 插件开发过程实例
限制每一条SQL返回数据的行数。限制的行数是个可配置的参数,业务可以根据自己的需要去配置。
7.5.1 确定需要拦截的签名
从Plugin源码中我们可以看到它需要注册签名才能够运行插件。签名需要确定一些要素。
(1) 确定需要拦截的签名

那么我们需要拦截的是StatementHandler对象,应该在预编译SQL之前,修改SQL使得返回数量被限制。
(2) 拦截方法和参数
查询是通过Executor调度StatementHandler来完成的。调度StatementHandler的prepare方法预编译SQL,于是我们需要拦截的方法便是prepare方法,在此之前完成SQL的重新编译。先来看看那StatementHandler接口的定义:

以上的任何方法都可以被拦截。从接口的定义而言,prepare方法有一个参数Connection对象,因此我们按如下代码来设计拦截器:

其中@Intercepts说明它是一个拦截器。@Signature是注册拦截器签名的地方,只有签名满足条件才能被拦截,type可以使四大对象中的一个,这里是StatementHandler。method代表要拦截四大对象的某一种接口方法,而args则表示该方法的参数。
7.5.4 插件实例
限制查询返回的数据量。需要拦截的是StatementHandler对象




在setProperties方法中可以读入配置给插件的参数,一个是数据库的名称,另外一个是限制的记录数。在plugin方法里,使用了MyBatis提供的类来生成代理对象。那么插件就会进入plugin的invoke方法,它最后会使用到拦截器的intercept方法。现在我们需要在MyBatis配置文件里面配置才能运行这个插件:

注意:


《深入浅出MyBatis技术原理与实战》——7. 插件的更多相关文章
- 《深入浅出MyBatis技术原理与实战》——6. MyBatis的解析和运行原理
MyBatis的运行分为两大部分,第一部分是读取配置文件缓存到Configuration对象,用以创建SqlSessionFactory,第二部分是SqlSession的执行过程. 6.1 涉及的技术 ...
- 《深入浅出MyBatis技术原理与实战》——3. 配置
要注意的是上面那些层次是不能够颠倒顺序的,否则MyBatis在解析文件的时候就会出现异常. 3.1 properties元素 properties是一个属性配置元素,让我们能在配置文件的上下文中使用它 ...
- 《深入浅出MyBatis技术原理与实战》——4. 映射器,5. 动态SQL
4.1 映射器的主要元素 4.2 select元素 4.2.2 简易数据类型的例子 例如,我们需要统计一个姓氏的用户数量.应该把姓氏作为参数传递,而将结果设置为整型返回给调用者,如: 4.2.3 自动 ...
- 《深入浅出MyBatis技术原理与实战》——1.简介,2.入门
1. 简介 Java程序都是通过JDBC连接数据库,但是只定义了接口规范,具体的实现交给各个数据库厂商去实现,因为每个数据库都有其特殊性.所以JDBC是一种桥接模式. 这里为什么说JDBC是一种桥接模 ...
- 深入浅出MyBatis技术原理与实战
第1 章 MyBatis 简介..................................................................................... ...
- 深入浅出Mybatis技术原理与实战(杨开振)(带详细书签) PDF 下载 高清 完整版+源码
(杨开振) 源码 IDE eclipse 建表语句也在里面 电子书+源码地址
- 2017.2.9 深入浅出MyBatis技术原理与实践-第八章 MyBatis-Spring(二)-----配置文件详解
深入浅出MyBatis技术原理与实践-第八章 MyBatis-Spring(二) ------配置文件详解 8.2 MyBatis-Spring应用 8.2.1 概述 本文主要讲述通过注解配置MyBa ...
- 2MyBatis入门--深入浅出MyBatis技术原理与实践(笔记)
什么是 MyBatis ? MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis ...
- 3MyBatis配置--深入浅出MyBatis技术原理与实践(笔记)
XML 映射配置文件 configuration 配置 properties 属性 settings 设置 typeAliases 类型命名 typeHandlers 类型处理器 objectFact ...
随机推荐
- 项目管理---git----快速使用git笔记(七)------coding.net项目管理多人操作的流程规范--合并代码审核
我们在前面已经介绍了coding.net和本地git的基本用法. 但是多人协作开发时情况会复杂得多,所以我们最好有一些规范来保证项目多人开发顺利进行. 比如说 规范一 master代码分支 需要开启 ...
- STL之四:list用法详解
转载于:http://blog.csdn.net/longshengguoji/article/details/8520891 list容器介绍 相对于vector容器的连续线性空间,list是一个双 ...
- [洛谷P3629] [APIO2010]巡逻
洛谷题目链接:[APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以 ...
- Strand Sort
Strand sort是思路是这样的,它首先需要一个空的数组用来存放最终的输出结果,给它取个名字叫"有序数组" 然后每次遍历待排数组,得到一个"子有序数组",然 ...
- JNLP Slave connection error解决办法
Replace in jnlp-file <argument>-workDir</argument> <argument /> with <argume ...
- 要back的题目 先立一个flag
要back的题目 目标是全绿!back一题删一题! acmm7 1003 1004 acmm8 1003 1004 sysu20181013 Stat Origin Title Solved A Gy ...
- 【BZOJ1272】Gate Of Babylon [Lucas][组合数][逆元]
Gate Of Babylon Time Limit: 10 Sec Memory Limit: 162 MB[Submit][Status][Discuss] Description Input ...
- 【codevs】3196 黄金宝藏
[算法]区间DP+博弈论 [题解]其实它都不是博弈题…… 很自然的可以设f[i][j]表示i~j先手可取得的最大价值. 容易得到转移式:f[i][j]=max(a[i]+sum[i+1~j]-f[i+ ...
- 代理设计模式iOS开发Demo(示例程序)源代码
iOS程序源代码下载链接:03-代理设计模式.zip28.3 KB // main.m // // main.m // 03-代理设计模式 // // Created by apple ...
- zuul进行rate limit
maven <dependency> <groupId>com.marcosbarbero.cloud</groupId> <artifactId>sp ...