博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【深入浅出MyBatis笔记】插件
阅读量:6147 次
发布时间:2019-06-21

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

插件

1、插件接口

在MyBatis中使用插件,我们必须实现接口Interceptor。

public interface Interceptor {  // 它将直接覆盖你所拦截对象原有的方法,因此它是插件的核心方法。  // Intercept里面有个参数Invocation对象,通过它可以反射调度原来对象的方法  Object intercept(Invocation invocation) throws Throwable;  // 作用是给被拦截对象生成一个代理对象,并返回它。target是被拦截对象  Object plugin(Object target);  // 允许在plugin元素中配置所需参数,方法在插件初始化的时候就被调用了一次  void setProperties(Properties properties);}

2、插件初始化

插件的初始化是在MyBatis初始化的时候完成的。

public class XMLConfigBuilder extends BaseBuilder {  ......  private void pluginElement(XNode parent) throws Exception {    if (parent != null) {      for (XNode child : parent.getChildren()) {        String interceptor = child.getStringAttribute("interceptor");        Properties properties = child.getChildrenAsProperties();        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();        interceptorInstance.setProperties(properties);        configuration.addInterceptor(interceptorInstance);      }    }  }}

在解析配置文件的时候,在MyBatis的上下文初始化过程中,就开始读入插件节点和我们配置的参数,同时使用反射技术生成对应的插件实例,然后调用插件方法中的setProperties方法,设置我们配置的参数,然后将插件实例保存到配置对象中,以便读取和使用它。

插件在Configuration对象中的保存:

public void addInterceptor(Interceptor interceptor) {    interceptorChain.addInterceptor(interceptor);  }

3、插件的代理和反射设计

插件用的是责任链模式,MyBatis的责任链是由interceptorChain去定义的。在MyBatis创建Executor执行器的时候,我们可以看到有如下的一段代码:

executor = (Executor) interceptorChain.pluginAll(executor);

再看下Interceptor的pluginAll方法:

public class InterceptorChain {  private final List
interceptors = new ArrayList
(); public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { // plugin方法是生成代理对象的方法 // 可以看出来,如果有多个插件的话,会生成多层代理的代理对象 target = interceptor.plugin(target); } return target; } ......}

MyBatis为我们提供了Plugin类用于生成代理对象。

public class Plugin implements InvocationHandler {  ......  public static Object wrap(Object target, Interceptor interceptor) {    Map
, Set
> signatureMap = getSignatureMap(interceptor); Class
type = target.getClass(); Class
[] interfaces = getAllInterfaces(type, signatureMap); if (interfaces.length > 0) { return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } return target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Set
methods = signatureMap.get(method.getDeclaringClass()); // 如果存在签名的拦截方法,插件的intercept方法将被调用 if (methods != null && methods.contains(method)) { return interceptor.intercept(new Invocation(target, method, args)); } // 否则,直接反射调度我们要执行的方法 return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } }}

在调用插件的拦截方法时,可以看到传递了一个新创建的Invocation对象。

interceptor.intercept(new Invocation(target, method, args));

Invocation类封装了被代理的对象、方法及其参数。

public class Invocation {  public Invocation(Object target, Method method, Object[] args) {    this.target = target;    this.method = method;    this.args = args;  }  // 这个方法会调度被代理对象的真实方法, 所以我们通过这个方法直接调用被代理对象原来的方法  // 如果多个插件的话,我们知道会生成多层代理对象,那么每层被代理都可以通过Invocation调用这个proceed方法,  // 所以在多个插件的环境下,调度proceed()方法时,MyBatis总是从最后一个代理对象运行到第一个代理对象,  // 最后是真实被拦截的对象方法被运行  public Object proceed() throws InvocationTargetException, IllegalAccessException {    return method.invoke(target, args);  }}

转载地址:http://egmya.baihongyu.com/

你可能感兴趣的文章
Windows DHCP Server基于MAC地址过滤客户端请求实现IP地址的分配
查看>>
命令查询每个文件文件数
查看>>
《跟阿铭学Linux》第8章 文档的压缩与打包:课后习题与答案
查看>>
RAC表决磁盘管理和维护
查看>>
Apache通过mod_php5支持PHP
查看>>
发布一个TCP 吞吐性能测试小工具
查看>>
java学习:jdbc连接示例
查看>>
PHP执行批量mysql语句
查看>>
Extjs4.1.x 框架搭建 采用Application动态按需加载MVC各模块
查看>>
Silverlight 如何手动打包xap
查看>>
建筑电气暖通给排水协作流程
查看>>
JavaScript面向对象编程深入分析(2)
查看>>
linux 编码转换
查看>>
POJ-2287 Tian Ji -- The Horse Racing 贪心规则在动态规划中的应用 Or 纯贪心
查看>>
Windows8/Silverlight/WPF/WP7/HTML5周学习导读(1月7日-1月14日)
查看>>
关于C#导出 文本文件
查看>>
使用native 查询时,对特殊字符的处理。
查看>>
maclean liu的oracle学习经历--长篇连载
查看>>
ECSHOP调用指定分类的文章列表
查看>>
分享:动态库的链接和链接选项-L,-rpath-link,-rpath
查看>>