博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
谈谈 MyBatis 的插件化设计
阅读量:6246 次
发布时间:2019-06-22

本文共 3360 字,大约阅读时间需要 11 分钟。

1.MyBatis 是什么  

MyBatis 是一个 ORM(Object Relational Mapping,对象 - 关系映射)框架,其中我们写的 Java 代码中的对象数据就是对象模型,关系数据库中的表数据就是关系模型。MyBatis 底层通过 JDBC 接口与关系数据库交互,主要功能是根据映射配置文件,完成数据在对象模型与关系模型之间的映射,减少了单纯用 JDBC 开发时的重复代码,只暴露简单的 API 供开发人员使用。 

 2.什么是插件

插件是一种常见的扩展方式,大多数开源框架也都支持用户通过添加自定义插件的方式来 扩展或改变框架原有的功能。 Mybatis 中也提供了插件的功能,虽然叫插件,但是实际上是通过 拦截器( Interceptor)实现的。在 MyBatis 的插件模块中涉及 责任链模式和 JDK 动态代理的知识, 下文会分析责任链模式和 JDK 动态代理在 MyBatis 插件模块中的实践。

 3.责任链模式介绍

 责任链模式主要用来处理 ”客户端发出一个请求,有多个对象都有机会来处理这一个请求,但是客户端不知道究竟谁会来处理他的请求“ 这样的情况。也就是需要让请求者和接收者解耦,这样就可以动态地切换和组合接收者了。 注意,在责任链模式中,请求不一定会被处理,因为可能没有合适的处理者,请求在责任链中从头传递到尾,每个处理对象都判断不属于自己处理,最后请求就没有对象来处理。 责任链模式的结构图如下:

 

当有请求进入时,经过 HandlerOne 的 handlerRequest 方法,再把请求传递给 HandlerTwo,处理完再把请求传递给 HandlerThree,以此类推,形成一个链条。链条上每一个对象所承担的责任各不相同,这就是责任链模式。 

 4.MyBatis 中的拦截器  

(1)拦截器的作用点

MyBatis允许用户使用自定义拦截器对 SQL语句执行过程中的某一点进行拦截。 默认情况 下, MyBatis 允许拦截器拦截 Executor 的方法、 ParameterHandler 的方法、 ResultSetHandler 的 方法 以及 StatementHandler 的方法。这里说明一下,阅读过 MyBatis 源码的同学应该都知道,这里的 Executor、ParameterHandler、ResultSetHandler 以及 StatementHandler 都是 MyBatis 操作 SQL 过程中的必备的核心层,而我们的拦截器也就是在这些接口的实现类的方法执行中起作用的。 具体可拦截的方法如下 : 

Executor:

         update()、query()、flushStatements()、commit()、rollback() 、 getTransaction()、 close()、 isClosed()方法 。

 ParameterHandler :

         getParameterObject()、 setParameters()方法 。 

ResultSetHandler:

        handleResultSets()、 handleOutputParameters()方法 。

 StatementHandler: 

        prepare()、 parameterize()、 batch()、 update()、 query()方法。

 

(2)拦截器定义

public interface Interceptor {     //执行拦截器方法     Object intercept(Invocation invocation) throws Throwable;     // 创建interceptor对象,一般用mybatis提供的Plugin.wrap(...)方法     // 该方法内部会判断,当前对象是否匹配当前interceptor,匹配则创建代理对象,不匹配则返回当前对象。     Object plugin(Object target);     // mybatis加载初始化时,会根据xml或注解的配置,将拦截器中的配置值取出,调用这个方法做初始化操作    void setProperties(Properties properties); }复制代码

 我们自定义的拦截器需要实现这个接口,并在 MyBatis 中做好配置,即可生效。  

(3)拦截器链

public class InterceptorChain {     // 所有已经配置的拦截器     private final List
interceptors = new ArrayList<>(); public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } // mybatis加载初始化时,会根据xml或注解的配置,创建拦截器,调用这个方法存入字段 interceptors 中 public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); } // 获取所有的拦截器 public List
getInterceptors() { return Collections.unmodifiableList(interceptors); } } 复制代码

MyBatis 在启动加载配置文件时将已配置的所有拦截器对象(包括自定义的拦截器对象)都放在 InterceptorChain 类对象的字段 interceptors 中供后续操作使用。 在 InterceptorChain 的 pluginAll 方法中,循环 interceptors,调用 interceptor.plugin(target) 方法,这个 plugin 方法内部会判断这个 target 对象是否匹配当前当前的 interceptor 对象,如果匹配则用 JDK 动态代理创建 target 对象的代理对象,否则返回未被代理的 target 对象。这里是循环 interceptors,所以每个 interceptor 都会去执行一次 plugin 方法,也就是上个 interceptor.plugin(target) 调用的返回值是下一个 interceptor.plugin(target) 的入参。最后结果就是 target 可能会被代理多次。如下图: 

结果看到了,最后的 target 对象可能是被多次代理过的对象,也就是图中的 interceptorProxy1 对象。当 interceptorProxy1 执行 interceptor 方法时,内部会调用到 interceptorProxy2 对象的 interceptor 方法, interceptorProxy2 的 interceptor 方法内部继续往下调直到最终的 target 对象调用。 说到这,可以看出 target 对象先是匹配所有适合自己的 interceptor 对象,然后在所有匹配的 interceptor 对象中流转。这就可以理解成责任链模式的设计。

 5.总结

关于 MyBatis 中拦截器的作用点,拦截器定义,以及拦截器的责任链模式在这里做了一定程度的讲解,下篇文章会补充拦截器的代理对象是如何创建的,也就是 interceptor.plugin(target); 代理的内部逻辑实现。

转载于:https://juejin.im/post/5cb6147f6fb9a0685255369b

你可能感兴趣的文章
# 2017-2018-1 20155224 《信息安全系统设计基础》第七周学习总结
查看>>
scikit-learn预处理实例之一:使用FunctionTransformer选择列
查看>>
Oracle11G 卸载步骤
查看>>
Mars说光场(3)— 光场采集
查看>>
中小企业客户营收增长将成微博2016年主推动力
查看>>
第一个掘金文章
查看>>
我的友情链接
查看>>
微信Windows版无法备份聊天记录
查看>>
Github上传代码菜鸟超详细教程
查看>>
资金项目性能优化
查看>>
Java将图片处理成背景透明的圆形图片
查看>>
知道IP地址怎么查看mac地址
查看>>
2016年度总结
查看>>
对于常见未得到支持操作的理解
查看>>
Win7 下Maple驱动问题解决方案
查看>>
hibernate 不输出sql参数的解决
查看>>
Netty的异步事件驱动(ChannelFuture)
查看>>
PostgreSQL数据类型-货币类型
查看>>
eclipse.ini、flashbuilder.ini内存设置和堆栈非堆栈问题
查看>>
线程下变量-原子操作 __sync_fetch_and_add等等
查看>>