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

被折叠的 条评论
为什么被折叠?



