大多数框架,都支持插件,用户可通过编写插件来自行扩展功能,Mybatis也不例外。

  我们从插件配置、插件编写、插件运行原理、插件注册与执行拦截的时机、初始化插件、分页插件的原理等六个方面展开阐述。

 一、插件配置

  Mybatis的插件配置在configuration内部,初始化时,会读取这些插件,保存于Configuration对象的InterceptorChain中。

1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
3 <configuration>
4 <plugins>
5 <plugin interceptor="com.mybatis3.interceptor.MyBatisInterceptor">
6 <property name="value" value="100" />
7 </plugin>
8 </plugins>
9 </configuration>
1 public class Configuration
2 {
3 protected final InterceptorChain interceptorChain = new
4 InterceptorChain();
5 }

org.apache.ibatis.plugin.InterceptorChain.java源码。

 1 public class InterceptorChain {
2
3 private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
4
5 public Object pluginAll(Object target) {
6 for (Interceptor interceptor : interceptors) {
7 target = interceptor.plugin(target);
8 }
9 return target;
10 }
11
12 public void addInterceptor(Interceptor interceptor) {
13 interceptors.add(interceptor);
14 }
15
16 public List<Interceptor> getInterceptors() {
17 return Collections.unmodifiableList(interceptors);
18 }
19
20 }

  上面的for循环代表了只要是插件,都会以责任链的方式逐一执行,所谓插件,其实就类似于拦截器。

二、插件的编写

  插件必须实现org.apache.ibatis.plugin.Interceptor接口。

1 public interface Interceptor {
2
3 Object intercept(Invocation invocation) throws Throwable;
4
5 Object plugin(Object target);
6
7 void setProperties(Properties properties);
8
9 }

-intercept()方法:执行拦截内容的地方,拦截目标对象的目标方法的执行

-plugin()方法:决定是否触发intercept()方法。  作用:包装目标对象,包装就是为目标对象创建一个代理对象

-setProperties()方法:给自定义的拦截器传递xml配置的属性参数。将插件注册时的property属性设置进来

下面自定义一个拦截器:

 1 package com.atguigu.mybatis.dao;
2
3 import java.util.Properties;
4
5 import org.apache.ibatis.executor.statement.StatementHandler;
6 import org.apache.ibatis.plugin.Interceptor;
7 import org.apache.ibatis.plugin.Intercepts;
8 import org.apache.ibatis.plugin.Invocation;
9 import org.apache.ibatis.plugin.Plugin;
10 import org.apache.ibatis.plugin.Signature;
11 import org.apache.ibatis.reflection.MetaObject;
12 import org.apache.ibatis.reflection.SystemMetaObject;
13
14
15 /*
16 * 完成插件签名:
17 * 告诉Mybatis当前插件用来拦截哪个对象的哪个方法
18 *
19 * */
20 @Intercepts(
21 {
22 @Signature(type=StatementHandler.class,method ="parameterize",args = java.sql.Statement.class )
23 }
24 )
25 public class MyFirstPlugin implements Interceptor
26 {
27 /*
28 * 拦截目标对象的目标方法的执行
29 * */
30 @Override
31 public Object intercept(Invocation invocation) throws Throwable
32 {
33 System.out.println("MyFirstPlugin的intercept方法执行,拦截方法:"+invocation.getMethod());
34
35 //动态改变一下sql的参数,查3号员工,这里偷梁换柱一下查询4号员工
36 Object target = invocation.getTarget();
37 System.out.println("当前拦截的对象:"+target);
38 //拿到:StatementHandler==>ParameterHandler==>parameterObject
39 //拿到target(拦截对象)的元数据
40 MetaObject metaObject = SystemMetaObject.forObject(target);
41 Object value = metaObject.getValue("parameterHandler.parameterObject");
42 System.out.println("sql语句用的参数是:"+value);
43 metaObject.setValue("parameterHandler.parameterObject", 4);
44
45 //执行目标方法
46 Object proceed = invocation.proceed();
47
48
49
50 //返回执行后的返回值
51 return proceed;
52 }
53
54
55 /*
56 * 插件:
57 * 作用:包装目标对象,包装就是为目标对象创建一个代理对象
58 * */
59 @Override
60 public Object plugin(Object target)
61 {
62 System.out.println("MyFirstPlugin的plugin方法执行,包装的对象(目标对象):"+target);
63
64
65 //我们可以借助Plugin的wrap方法来使用当前的拦截器包装我们的目标对象
66 Object wrap = Plugin.wrap(target, this);
67
68 //返回的就是为当前target创建的动态代理
69 return wrap;
70 }
71
72 /*
73 * setProperties:
74 * 将插件注册时的property属性设置进来
75 *
76 * */
77 @Override
78 public void setProperties(Properties properties)
79 {
80 System.out.println("插件配置的信息:"+properties);
81 }
82
83 }

2.1、为什么要写Annotation注解?注解都是什么含义?

  Mybatis规定插件必须编写Annotation注解,是必须,而不是可选。@Intercepts注解:装载一个@Signature列表,一个@Signature其实就是一个需要拦截的方法封装。那么,一个拦截器要拦截多个方法,自然就是一个@Signature列表。

  type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }

  解释:要拦截Executor接口内的query()方法,参数类型为args列表。

2.2、Plugin.wrap(target, this)是干什么的?

  使用JDK的动态代理,给target对象创建一个delegate代理对象,以此来实现方法拦截和增强功能,它会回调intercept()方法。

2.3、Mybatis可以拦截哪些接口对象?

  Mybatis只能拦截ParameterHandler、ResultSetHandler、StatementHandler、Executor共4个接口对象内的方法。

  重新审视interceptorChain.pluginAll()方法:该方法在创建上述4个接口对象时调用,其含义为给这些接口对象注册拦截器功能,注意是注册,而不是执行拦截。

  拦截器执行时机:plugin()方法注册拦截器后,那么,在执行上述4个接口对象内的具体方法时,就会自动触发拦截器的执行,也就是插件的执行。

2.4、Invocation

1 public class Invocation {
2 private Object target;
3 private Method method;
4 private Object[] args;
5 }

  可以通过invocation来获取拦截的目标方法,以及执行目标方法。

2.5、分页插件原理

  由于Mybatis采用的是逻辑分页,而非物理分页,那么,市场上就出现了可以实现物理分页的Mybatis的分页插件。  要实现物理分页,就需要对String sql进行拦截并增强,Mybatis通过BoundSql对象存储String sql,而BoundSql则由StatementHandler对象获取。

1 public interface StatementHandler {
2 <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
3
4 BoundSql getBoundSql();
5 }
1 public class BoundSql {
2 public String getSql() {
3 return sql;
4 }
5 }

  因此,就需要编写一个针对StatementHandler的query方法拦截器,然后获取到sql,对sql进行重写增强。

Mybatis之plugin插件设计原理的更多相关文章

  1. Mybatis log plugin插件破解修复版 MyBatis Log Plugin License Authorization Failed

    github地址 - https://github.com/Link-Kou/intellij-mybaitslog

  2. 互联网轻量级框架SSM-查缺补漏第八天(MyBatis插件plugin使用及原理)

    简言:今天进行第八天的记录(只是写了八天).有的时候看的多,有的时候看的少,看的少的时候就攒几天一起写了.而今天这个插件我昨天写了一下午,下班没写完就回去了,今天把尾收了,再加上一个过程图方便下面原理 ...

  3. 【MyBatis源码分析】插件实现原理

    MyBatis插件原理----从<plugins>解析开始 本文分析一下MyBatis的插件实现原理,在此之前,如果对MyBatis插件不是很熟悉的朋友,可参看此文MyBatis7:MyB ...

  4. Mybatis九( mybatis插件的原理及使用)

    1.插件执行原理 一.demo 1.测试类 @Test public void test1() { String resource = "mybatis-config.xml"; ...

  5. Mybatis源码分析之插件的原理

    MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用. 默认情况下,可以使用插件来拦截的方法调用包括: Executor (update, query, flushStatements, ...

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

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

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

    本文主要讲解MyBatis非常棒的缓存机制的设计原理,给读者们介绍一下MyBatis的缓存机制的轮廓,然后会分别针对缓存机制中的方方面面展开讨论. MyBatis将数据缓存设计成两级结构,分为一级缓存 ...

  8. mybatis 插件的原理-责任链和动态代理的体现

    目录 1 拦截哪些方法 2 如何代理 3 代理对象 4 责任链设计模式 @ 如果没有自定义过拦截器, 可以看我前面的文章.如果不知道 JDK 动态代理怎么使用的, 可以看我这文章. 责任链设计模式理解 ...

  9. 送命题:讲一讲Mybatis插件的原理及如何实现?

    持续原创输出,点击上方蓝字关注我吧 目录 前言 环境配置 什么是插件? 如何自定义插件? 举个栗子 用到哪些注解? 如何注入Mybatis? 测试 插件原理分析 如何生成代理对象? 如何执行? 总结 ...

随机推荐

  1. C++逐字输出函数

    void fun(string a) { for(int i=0;i<a.length();i++) { cout<<a[i]; usleep(10000); } cout<& ...

  2. 04 sublime text 3在线安装package control插件,之后安装主题插件和ConvertToUTF8 插件

    前提:需要@@科学@@上网 在线安装包通常都需要@@科学@@上网 安装package control插件 在线安装package control插件 按ctrl+shift+p 输入install,选 ...

  3. C++读写ini配置文件GetPrivateProfileString()&WritePrivateProfileString()

    转载: 1.https://blog.csdn.net/fengbingchun/article/details/6075716 2. 转自:http://hi.baidu.com/andywangc ...

  4. Linux就该这么学28期——Day02 2.1-2.3

    本文记录必须掌握的Linux命令,部分内容引用自https://www.linuxprobe.com/basic-learning-02.html 工作中可使用https://www.linuxcoo ...

  5. STM32F103C8T6-CubeMx串口收发程序详细设计与测试(1)——CubeMx生成初始代码

    STM32F103C8T6-CubeMx串口收发程序详细设计与测试(1)--CubeMx生成初始代码 关键词:STM32F103C8T6 CubeMX UART 详细程序设计 1.开发环境 (1)ST ...

  6. 极客 Play 玩 Terminal——GitHub 热点速览 Vol.40

    作者:HelloGitHub-小鱼干 多少人以为暗黑的终端便是一名程序员的工作台,其实上,终端可以不只是一个输入 command 的界面,也可以是本周特推 kb 一样,面向极客的极简命令行知识库管理器 ...

  7. 设置 eclipse C++ 版本

    gcc 版本 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC) 默认是使用 C++ 98 版本进行编译 设置 eclipse 中 C++ 的版本: Project  -& ...

  8. 透视HTTPS建造固若金汤的城堡

    为什么有 HTTPS?因为 HTTP 不安全! 现在的互联网已经不再是 "田园时代","黑暗森林" 已经到来.上网的记录会被轻易截获,网站是否真实也无法验证,黑 ...

  9. 1T数据快速排序!十种经典排序算法总结

    1 冒泡排序 每次循环都比较前后两个元素的大小,如果前者大于后者,则将两者进行交换.这样做会将每次循环中最大的元素替换到末尾,逐渐形成有序集合.将每次循环中的最大元素逐渐由队首转移到队尾的过程形似&q ...

  10. C 语言因为疫情重登最流行编程语言榜第一名!其实它一直都在~

      C 语言时隔五年后再次荣登最流行编程语言榜单第一名. 通过调查得出结论, C 语言再次受欢迎的原因竟然是因为病毒,这听起来愚蠢但确实是事实,嵌入式语言 C 和 C++ 被用于医疗设备中的幅度因为疫 ...