mybatis中的plugins原理

mybatis的plugins(插件)是用来对mybatis运行中的方法进行拦截的,其主要拦截的方法有:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

plugins从创建到运行主要分为了三个部分:

  • 解析Plugins配置生成Intercpetor责任链
  • 创建Executor,ParameterHandler,ResultSetHandler ,StatementHandler实现类时,将实现类的实例与Intercpetor生成嵌套代理对象(此代理对象就像一颗洋葱,层层包裹,最里面才是被代理的对象)
  • 代理对象被调用时Interceptor执行Intercepts进行拦截处理

解析Plugins配置生成Intercpetor责任链

  1. Configuration在创建时会解析mybatis-config.xml中的<plugins>标签中的plugin,其中plugin中指向的类都是Interceptor的实现类,在解析是会调用Configuration的addInterceptor将其实例化后全部加入Configuration中的InterceptorChain中.

    private void pluginElement(XNode parent) throws Exception {
        if (parent != null) {
          for (XNode child : parent.getChildren()) {
            String interceptor = child.getStringAttribute("interceptor");
            ...
            Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor)
                                                                .newInstance();
            ...
            configuration.addInterceptor(interceptorInstance);
          }
        }
      }
    
  2. Configuration中创建Executor,StatementHandler,parameterHandler,ResultSetHandler时调用对应的newXXX方法,方法中调用Configuration中的属性interceptorChains的pluginAll方法

    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
      ...
      executor = (Executor) interceptorChain.pluginAll(executor);
      ...
    }
    
  3. InterceptorChain中的pluginAll方法运行时会去遍历Interceptor集合,调用集合中对象的plugin方法,而这些对象都是Interceptor的实现类,实现类中的plugin方法会去调用Plugin代理类的warp方法,进行当前Interceptor实现类的代理对象生成。

org.apache.ibatis.plugin.InterceptorChain#pluginAll


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

注意target在循环中被赋值,说明target对象在进行代理嵌套

org.apache.ibatis.plugin.Interceptor(实现类)#plugin

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

其中plugin方法的参数target为第二步调用pluginAll时传入的创建对象(Executor,StatementHandler,

parameterHandler,ResultSetHandler)或者为warp后的代理对象。

生成嵌套代理对象

首先,我们要明确一点,Plugin类是一个JDK代理类应为他实现了InvocationHandler,只是这个代理类有点不一样,比我们认识的单纯的代理类多了一些东西。

  1. org.apache.ibatis.plugin.Plugin#getSignatureMap 方法,此方法用于打包Interceptor实现类中的Signature注解集合。

    首先我们看一下Signature注解中有哪些属性

    public @interface Signature {
      Class<?> type();//要被拦截的对象
    
      String method();//要被拦截对象中的具体方法名称
    
      Class<?>[] args();//要被拦截对象中方法的具体参数对象集合
    }
    

    通过以上三点,来确定需要拦截的方法到底是哪一个

    回过头来我们继续看org.apache.ibatis.plugin.Plugin#getSignatureMap 方法,此方法获取Interceptor实现类的注解

     Intercepts interceptsAnnotation = interceptor.getClass()
                                                      .getAnnotation(Intercepts.class);
        ...
        Signature[] sigs = interceptsAnnotation.value();
    

    如上我们获得了一个Sigature注解的数组,遍历Signature数组通过Signature中的三个属性,我们将解析后的数据存放到一个以类为Key,方法集合为value的Map中

    Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
        for (Signature sig : sigs) {
          Set<Method> methods = signatureMap.get(sig.type());
          if (methods == null) {
            methods = new HashSet<Method>();
            signatureMap.put(sig.type(), methods);
          }
          try {
            Method method = sig.type().getMethod(sig.method(), sig.args());
            methods.add(method);
          } catch (NoSuchMethodException e) {
            throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
          }
        }
    

    最终返回此Map,后面拦截器就是以此作为是否拦截的依据。

  2. org.apache.ibatis.plugin.Plugin#getAllInterfaces方法,此方法用于获取被代理类满足拦截条件的父接口。其中此方法是通过org.apache.ibatis.plugin.Plugin#getSignatureMap返回值作为判断的依据进行接口的筛选。

  3. 通过2中获取到的被代理类满足拦截条件的父接口数组,

    • 如果此数组为空,则被代理对象不需要进行拦截处理,那么直接返回原对象就可以.
    return target;
    
    • 如果数组不为空,则此代理对象需要进行拦截处理,那么则生成并返回代理对象。
    return Proxy.newProxyInstance(
              type.getClassLoader(),
              interfaces,
              new Plugin(target, interceptor, signatureMap));
    

至此Executor,StatementHandler,parameterHandler,ResultSetHandler代理对象的创建过程就结束了

代理链拦截处理

​ 当代理对象的方法被调用是,则会触发invoke方法,此时invoke方中通过signatureMap来判断此类和方法是否满足拦截要求

  • 如果不满足要求,则直接执行方法

    return method.invoke(target, args);
    
  • 如果满足要求则执行拦截器方法,并将当前被代理对象,方法, 参数值传递给拦截器

           Set<Method> methods = signatureMap.get(method.getDeclaringClass());
    
            if (methods != null && methods.contains(method)) {
    
              return interceptor.intercept(new Invocation(target, method, args));
    
            }
    

    ​ intercpet执行到最后一步,调用Invocation实例的proceed方法进行此责任链的下一层继续进行拦截,直到责任链走完(实现类的intercpet需要显示的调用Invocation的proceed方法)。

转载于:https://my.oschina.net/u/3518687/blog/1794201

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值