深入理解mybatis原理(十一) Mybatis插件原理之实现细节
来源:互联网 发布:马自达cx4 知乎 编辑:程序博客网 时间:2024/06/03 18:48
Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,以便写出安全高效的插件。
一、代理链的生成
Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理。下面以Executor为例。Mybatis在创建Executor对象时会执行下面一行代码:InterceptorChain里保存了所有的拦截器,它在mybatis初始化的时候创建。上面这句代码的含义是调用拦截器链里的每个拦截器依次对executor进行plugin(插入?)代码如下:
下面以一个简单的例子来看看这个plugin方法里到底发生了什么。
每一个拦截器都必须实现上面的三个方法,其中:
1) Object intercept(Invocation invocation)是实现拦截逻辑的地方,内部要通过invocation.proceed()显式地推进责任链前进,也就是调用下一个拦截器拦截目标方法。
2) Object plugin(Object target) 就是用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target,this) 来完成的,把目标target和拦截器this传给了包装函数。
3) setProperties(Properties properties)用于设置额外的参数,参数配置在拦截器的Properties节点里。
注解里描述的是指定拦截方法的签名 [type,method,args] (即对哪种对象的哪种方法进行拦截),它在拦截前用于决断。
二、Plugin.wrap方法
从前面可以看出,每个拦截器的plugin方法是通过调用Plugin.wrap方法来实现的。代码如下:
这个Plugin类有三个属性:
private Object target;//被代理的目标类
private Interceptor interceptor;//对应的拦截器
private Map<Class<?>, Set<Method>> signatureMap;//拦截器拦截的方法缓存
我们再次结合(Executor)interceptorChain.pluginAll(executor)这个语句来看,这个语句内部对
executor执行了多次plugin,第一次plugin后通过Plugin.wrap方法生成了第一个代理类,姑且就叫executorProxy1,这个代理类的target属性是该executor对象。第二次plugin后通过Plugin.wrap方法生成了第二个代理类,姑且叫executorProxy2,这个代理类的target属性是executorProxy1...这样通过每个代理类的target属性就构成了一个代理链(从最后一个executorProxyN往前查找,通过target属性可以找到最原始的executor类)。
三、代理链上的拦截
代理链生成后,对原始目标的方法调用都转移到代理者的invoke方法上来了。Plugin作为InvocationHandler的实现类,他的invoke方法是怎么样的呢?在每个拦截器的intercept方法内,最后一个语句一定是returninvocation.proceed()(不这么做的话拦截器链就断了,你的mybatis基本上就不能正常工作了)。invocation.proceed()只是简单的调用了下target的对应方法,如果target还是个代理,就又回到了上面的Plugin.invoke方法了。这样就形成了拦截器的调用链推进。
四、总结
我们假设在MyBatis配置了一个插件,在运行时会发生什么?1) 所有可能被拦截的处理类都会生成一个代理
2) 处理类代理在执行对应方法时,判断要不要执行插件中的拦截方法
3) 执行插接中的拦截方法后,推进目标的执行
如果有N个插件,就有N个代理,每个代理都要执行上面的逻辑。这里面的层层代理要多次生成动态代理,是比较影响性能的。虽然能指定插件拦截的位置,但这个是在执行方法时动态判断,初始化的时候就是简单的把插件包装到了所有可以拦截的地方。
因此,在编写插件时需注意以下几个原则:
1) 不编写不必要的插件;
2) 实现plugin方法时判断一下目标类型,是本插件要拦截的对象才执行Plugin.wrap方法,否者直接返回目标本省,这样可以减少目标被代理的次数。
- 《深入理解mybatis原理(十一)》 Mybatis插件原理之实现细节
- 深入理解mybatis原理(十一) Mybatis插件原理之实现细节
- 《深入理解mybatis原理(十)》 Mybatis插件原理简单分析
- 深入理解mybatis原理(十) Mybatis插件原理简单分析
- 《深入理解mybatis原理》
- 深入理解mybatis原理
- SSM 之《深入理解mybatis原理》 MyBatis缓存机制的设计与实现
- 《深入理解mybatis原理》 MyBatis事务管理机制
- 《深入理解mybatis原理》 MyBatis事务管理机制
- 深入理解mybatis原理》 MyBatis事务管理机制
- 《深入理解mybatis原理》 MyBatis事务管理机制
- 《深入理解mybatis原理》 MyBatis事务管理机制
- 《深入理解mybatis原理》 MyBatis事务管理机制
- Mybatis实现原理深入解析
- Mybatis实现原理深入解析
- 《深入理解mybatis原理》 MyBatis的二级缓存的设计原理
- 《深入理解mybatis原理》 MyBatis的二级缓存的设计原理
- 《深入理解mybatis原理》 MyBatis的二级缓存的设计原理
- Unity Mecanim动画的实现(七):动画曲线
- Apple Tree
- Avro文件操作示例代码
- error: protocol fault (couldn't read status): Connection reset by peer
- Mac安装MySQL初始密码设置
- 深入理解mybatis原理(十一) Mybatis插件原理之实现细节
- 堆的创建,插入,删除
- java 为什么要用封装类
- qte开发环境配置
- oracle同义词
- 深入理解mybatis原理(十二) mybatis深入理解之#与$区别
- 复习
- future
- Spring配置之hibernate事物管理AOP