MyBatis源码的学习(16)---拦截器为何不能拦截Executor中6个参数的query方法

本文探讨了在MyBatis中,为什么拦截器无法拦截Executor接口中带有六个参数的query方法。通过分析源码,发现拦截器在拦截方法内部调用时,由于调用者不是代理对象导致无法拦截。为解决这个问题,介绍了通过双重代理的方式,成功拦截并修改四个参数query方法的行为,实现特定功能增强。

今天,配置了一个MyBatis拦截器如下:

package learn.interceptors;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

@Intercepts({@Signature(
  type= Executor.class,
  method = "query",
  args = {MappedStatement.class,Object.class
    ,RowBounds.class,ResultHandler.class,
    CacheKey.class,BoundSql.class})})
public class ExecutorQueryInterceptor implements Interceptor {


  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    System.out.println(invocation.getMethod());
    System.out.println(invocation.getTarget());
    System.out.println(invocation.getArgs());
    return invocation.proceed();
  }
}

总配置文件中的插件配置:
 <plugins>
    <plugin interceptor="learn.interceptors.ExecutorQueryInterceptor"></plugin>
  </plugins>

本来想的是,能够拦截到这个六个参数的方法,然后做一些功能的增强。结果断点发现一直不进入代理类的执行方法中

 首先,在Plugin这个类中维护了一个HashSet类型的对象:methods。然后这个methods来自我们的signatureMap这个map中,

在这个map中key存放的是我们注解中的type的值,也就是我们会对Executor执行器进行拦截,然后value中的值就是要拦截的这个接口中的方法的列表,像我这个就是要拦截带6个参数的query方法 。

断点的时候,我们的这个四个参数的方法,走的是代理类的方法调用,然后在判断query方法是否在我们的拦截方法列表中时,因为我们配的是六个参数的,所以,直接走的原生的方法。

到了这个调用六个参数的方法的时候,没有走代理对象的方法。仔细想想,我们这个方法属于目标方法的内部的调用,这个时候它的调用者对象也就是this对象,是cachingExecutor对象,而不是代理对象。所以不会被拦截。

public class Bean{  
public void method(){  
method2();  
}  
public void method2(){  
...  
}  
}  

也就是说我们的拦截器拦截的方法,调用内部的其他方法时,内部的方法是无法被拦截的。 

然而,市面上常见的分页插件这些,他们是如何拦截到的呢?最后查看源码,然后模仿它们的方法,顺利拦截到了6个参数的方法。主要逻辑就是:先写一个拦截器拦截6个参数的方法,返回代理对象proxy1,然后我们再对这个proxy1进行代理,生成proxy2对象,proxy2中拦截proxy1中的四个参数的query方法,重写里面的逻辑,主要是生成另外俩个参数,最终调用proxy1的六个参数的query方法。

proxy2.query(4);
   proxy1.query(6);
     target.query(6);
 <plugins>
    <plugin interceptor="learn.interceptors.ExecutorQueryInterceptor"></plugin>
    <plugin interceptor="learn.interceptors.ExecutorQueryInterceptorFor4params"></plugin>
  </plugins>
package learn.interceptors;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

@Intercepts({
 @Signature(
  type= Executor.class,
  method = "query",
  args = {MappedStatement.class,Object.class
    ,RowBounds.class,ResultHandler.class,
    CacheKey.class,BoundSql.class})})
public class ExecutorQueryInterceptor implements Interceptor {

  // 这个类就是我们的 proxy1代理类
  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    System.out.println(invocation.getMethod());
    System.out.println(invocation.getTarget());
    System.out.println(invocation.getArgs());
    //这里可以对6个参数的query方法,进行调用前后的功能增强
    return invocation.proceed();
  }
}
package learn.interceptors;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

@Intercepts({@Signature(
  type= Executor.class,
  method = "query",
  args = {MappedStatement.class,Object.class
    ,RowBounds.class,ResultHandler.class})
    })
public class ExecutorQueryInterceptorFor4params implements Interceptor {

  /**
    *  这个类是我们的代理类 proxy2 
    * 重写四个参数的方法,这里面返回的是六个方法的调用
   * 我们对六个参数的query做了代理再做拦截
   * */
  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    //人为重写四个参数的方法,在这里调用六个参数的query
    Object[] args = invocation.getArgs();
    MappedStatement ms = (MappedStatement) args[0];
    Object parameter = args[1];
    RowBounds rowBounds = (RowBounds) args[2];
    ResultHandler resultHandler = (ResultHandler) args[3];
//这里面取出来的就是我们六个参数的query的代理对象,proxy1
    Executor executor = (Executor) invocation.getTarget();
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey cacheKey =executor.createCacheKey(ms, parameter, rowBounds, boundSql);
//proxy1的query()方法,我们在proxy1中重写这个方法就达到对原生六个参数方法的增强和拦截
    return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
  }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值