谈谈 MyBatis 的插件化设计

栏目: 数据库 · 发布时间: 5年前

内容简介:插件是一种常见的扩展方式,大多数开源框架也都支持用户通过添加自定义插件的方式来 扩展或改变框架原有的功能。 Mybatis 中也提供了插件的功能,虽然叫插件,但是实际上是通过 拦截器( Interceptor)实现的。在 MyBatis 的插件模块中涉及 责任链模式和 JDK 动态代理的知识, 下文会分析责任链模式和 JDK 动态代理在 MyBatis 插件模块中的实践。责任链模式主要用来处理 ”客户端发出一个请求,有多个对象都有机会来处理这一个请求,但是客户端不知道究竟谁会来处理他的请求“ 这样的情况。也

2.什么是插件

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

3.责任链模式介绍

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

谈谈 MyBatis 的插件化设计

当有请求进入时,经过 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 可能会被代理多次。如下图: 

谈谈 MyBatis 的插件化设计

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

5.总结

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


以上所述就是小编给大家介绍的《谈谈 MyBatis 的插件化设计》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

活着就为改变世界

活着就为改变世界

[美] 杰弗里·扬、威廉·西蒙 / 蒋永军 / 中信出版社 / 2010-6 / 39.00元

内容简介 苹果公司CEO史蒂夫•乔布斯这个一直活在自己想象的世界里的创业奇才,经历过各种挫折与失落,但他那无所畏惧、敢于承担的个性让他一直努力实践着自己的价值观,总能为他的离奇想法找到解决问题的办法。 本书两位作者通过深入访谈和资料调查,揭秘了许多乔布斯个人的创业经历和家庭变故,为大家塑造了一个活生生的“乔布斯式”的鲜活人物,描述了一个个充满传奇色彩的商业奇迹,真实地再现了乔布斯几十年......一起来看看 《活着就为改变世界》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具