mybatis(五):mybatis插件

在前面源码分析的过程中,有几次出现过一段代码InterceptorChain.pluginAll(),而这就是使用插件通过动态代理的方式对mybatis进行功能增强,我们平时使用的一些分页插件、分库分表插件等基本都是使用的mybatis plugin功能。接下来就来对这个过程一探究竟。
在之前的配置文件解析中提到过,pluginElement(root.evalNode("plugins")),这段代码就是用来解析插件配置的。类似于以下代码


    
      
    

先看下是如何解析和保存插件配置的

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).getDeclaredConstructor().newInstance();
        // 设置属性
        interceptorInstance.setProperties(properties);
        // 保存插件到 InterceptorChain.interceptors 插件链中
        configuration.addInterceptor(interceptorInstance);
        }
    }
}

最终所有的插件类都被保存到了InterceptorChain.interceptors,这个插件链表中,接下来看看InterceptorChain.pluginAll方法。



可以看到的是,InterceptorChain.pluginAll在四个类被实例化的时候都有调用:ParameterHandler(SQL参数设置)、ResultSetHandler(SQL查询结果设置)、StatementHandler(执行SQL)、Executor(执行器,调用StatementHandler)。因此在这4个类的调用过程中可以通过自定义插件,实现一些特殊的功能。
继续看InterceptorChain.pluginAll的代码

public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
}

default Object plugin(Object target) {
    return Plugin.wrap(target, this);
}

public static Object wrap(Object target, Interceptor interceptor) {
    // key=代理的类,value=代理类的哪些方法
    // 如:key=Executor.class,value=update,query
    Map, Set> signatureMap = getSignatureMap(interceptor);
    // 代理的类
    Class type = target.getClass();
    // 获取存在于type,并且被代理的类
    Class[] interfaces = getAllInterfaces(type, signatureMap);
    // 返回代理类
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
}

看到这里就可以发现,就是使用JDK动态代理的方式对需要被代理的类,层层包装。继续看Plugin的Invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // 获取method对应类需要被代理的方法集合
      Set methods = signatureMap.get(method.getDeclaringClass());
      // 如果包含当前方法
      if (methods != null && methods.contains(method)) {
        // 执行代理类的intercept方法
        return interceptor.intercept(new Invocation(target, method, args));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
}

代码比较简单

你可能感兴趣的:(mybatis(五):mybatis插件)