内容简介:插件是一种常见的扩展方式,大多数开源框架也都支持用户通过添加自定义插件的方式来 扩展或改变框架原有的功能。 Mybatis 中也提供了插件的功能,虽然叫插件,但是实际上是通过 拦截器( Interceptor)实现的。在 MyBatis 的插件模块中涉及 责任链模式和 JDK 动态代理的知识, 下文会分析责任链模式和 JDK 动态代理在 MyBatis 插件模块中的实践。责任链模式主要用来处理 ”客户端发出一个请求,有多个对象都有机会来处理这一个请求,但是客户端不知道究竟谁会来处理他的请求“ 这样的情况。也
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<Interceptor> 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<Interceptor> 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); 代理的内部逻辑实现。
以上所述就是小编给大家介绍的《谈谈 MyBatis 的插件化设计》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
HTML5秘籍(第2版)
[美] Matthew MacDonald / 李松峰、朱巍、刘帅 / 人民邮电出版社 / 2015-4 / 89.00元
不依赖插件添加音频和视频,构建适用于所有浏览器的播放页面。 用Canvas创建吸引人的视觉效果,绘制图形、图像、文本,播放动画,运行交互游戏。 用CSS3将页面变活泼,比如添加新奇的字体,利用变换和动画添加吸引人的效果。 设计更出色的Web表单,利用HTML5新增的表单元素更加高效地收集访客信息。 一次开发,多平台运行,实现响应式设计,创建适配桌面计算机、平板电脑和智能手机......一起来看看 《HTML5秘籍(第2版)》 这本书的介绍吧!