Mybatis采取责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默许行动(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,以便写出安全高效的插件。
Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理。下面以Executor为例。Mybatis在创建Executor对象时会履行下面1行代码:
InterceptorChain里保存了所有的拦截器,它在mybatis初始化的时候创建。上面这句代码的含义是调用拦截器链里的每一个拦截器顺次对executor进行plugin(插入?)代码以下:
下面以1个简单的例子来看看这个plugin方法里到底产生了甚么。
每个拦截器都必须实现上面的3个方法,其中:
1) Object intercept(Invocation invocation)是实现拦截逻辑的地方,内部要通过invocation.proceed()显式地推动责任链前进,也就是调用下1个拦截器拦截目标方法。
2) Object plugin(Object target) 就是用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target,this) 来完成的,把目标target和拦截器this传给了包装函数。
3) setProperties(Properties properties)用于设置额外的参数,参数配置在拦截器的Properties节点里。
注解里描写的是指定拦截方法的签名 [type,method,args] (即对哪一种对象的哪一种方法进行拦截),它在拦截前用于决断。
从前面可以看出,每一个拦截器的plugin方法是通过调用Plugin.wrap方法来实现的。代码以下:
这个Plugin类有3个属性:
private Object target;//被代理的目标类
private Interceptor interceptor;//对应的拦截器
private Map
我们再次结合(Executor)interceptorChain.pluginAll(executor)这个语句来看,这个语句内部对
executor履行了屡次plugin,第1次plugin后通过Plugin.wrap方法生成了第1个代理类,姑且就叫executorProxy1,这个代理类的target属性是该executor对象。第2次plugin后通过Plugin.wrap方法生成了第2个代理类,姑且叫executorProxy2,这个代理类的target属性是executorProxy1...这样通过每一个代理类的target属性就构成了1个代理链(从最后1个executorProxyN往前查找,通过target属性可以找到最原始的executor类)。
代理链生成后,对原始目标的方法调用都转移到代理者的invoke方法上来了。Plugin作为InvocationHandler的实现类,他的invoke方法是怎样样的呢?