设为首页 收藏本站
查看: 1261|回复: 0

[经验分享] Mybatis Interceptor

[复制链接]

尚未签到

发表于 2016-11-24 05:42:17 | 显示全部楼层 |阅读模式
  Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理。下面以Executor为例。Mybatis在创建Executor对象时会执行下面一行代码:



executor =(Executor) interceptorChain.pluginAll(executor);
  
interceptorChain里保存了所有的拦截器,它在mybatis初始化的时候创建。上面这句代码的含义是调用拦截器链里的每个拦截器依次对executor进行plugin(插入?)代码如下:



* 每一个拦截器对目标类都进行一次代理
* @paramtarget
* @return 层层代理后的对象
*/  
public Object pluginAll(Object target) {  
for(Interceptor interceptor : interceptors) {  
target= interceptor.plugin(target);  
}  
return target;  
}
  
举例:

@Intercepts({@Signature(type = Executor.class, method ="update";, args = {MappedStatement.class, Object.class})})
public class ExamplePlugin implements Interceptor {
@Override
public Objectintercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
@Override
public Objectplugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public voidsetProperties(Properties properties) {
}
}
  
每一个拦截器都必须实现上面的三个方法,其中:





    • 1)       Object intercept(Invocation invocation)是实现拦截逻辑的地方,内部要通过invocation.proceed()显式地推进责任链前进,也就是调用下一个拦截器拦截目标方法。
    • 2)       Object plugin(Object target) 就是用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target,this) 来完成的,把目标target和拦截器this传给了包装函数。
    • 3)       setProperties(Properties properties)用于设置额外的参数,参数配置在拦截器的Properties节点里。


Plugin.wrap方法

从前面可以看出,每个拦截器的plugin方法是通过调用Plugin.wrap方法来实现的。代码如下:

    public staticObject wrap(Object target, Interceptor interceptor) {
//从拦截器的注解中获取拦截的类名和方法信息
Map<Class<?>, Set<Method>> signatureMap =getSignatureMap(interceptor);
Class<?> type = target.getClass();
//解析被拦截对象的所有接口(注意是接口)
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if(interfaces.length > 0) {
//生成代理对象, Plugin对象为该代理对象的InvocationHandler  (InvocationHandler属于java代理的一个重要概念,不熟悉的请参考相关概念)
returnProxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target,interceptor,signatureMap));
}
returntarget;
}
 这个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方法是怎么样的呢?

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if(methods != null && methods.contains(method)) {
//调用代理类所属拦截器的intercept方法,
return interceptor.intercept(new Invocation(target, method, args));
}
returnmethod.invoke(target, args);
} catch(Exception e) {
throwExceptionUtil.unwrapThrowable(e);
}
}
 invoke里,如果方法签名和拦截中的签名一致,就调用拦截器的拦截方法。我们看到传递给拦截器的是一个Invocation对象,这个对象是什么样子的,他的功能又是什么呢?

public class Invocation {
private Object target;
private Methodmethod;
private Object[]args;
publicInvocation(Object target, Method method, Object[] args) {
this.target =target;
this.method =method;
this.args =args;
}
...
public Objectproceed() throws InvocationTargetException, IllegalAccessException {
returnmethod.invoke(target, args);
}
}
 
可以看到,Invocation类保存了代理对象的目标类,执行的目标类方法以及传递给它的参数。
在每个拦截器的intercept方法内,最后一个语句一定是returninvocation.proceed()(不这么做的话拦截器链就断了,你的mybatis基本上就不能正常工作了)。invocation.proceed()只是简单的调用了下target的对应方法,如果target还是个代理,就又回到了上面的Plugin.invoke方法了。这样就形成了拦截器的调用链推进。
 

总结

我们假设在MyBatis配置了一个插件,在运行时会发生什么?
1)       所有可能被拦截的处理类都会生成一个代理
2)       处理类代理在执行对应方法时,判断要不要执行插件中的拦截方法
3)       执行插接中的拦截方法后,推进目标的执行
如果有N个插件,就有N个代理,每个代理都要执行上面的逻辑。这里面的层层代理要多次生成动态代理,是比较影响性能的。虽然能指定插件拦截的位置,但这个是在执行方法时动态判断,初始化的时候就是简单的把插件包装到了所有可以拦截的地方。
因此,在编写插件时需注意以下几个原则:
1)       不编写不必要的插件;
2)       实现plugin方法时判断一下目标类型,是本插件要拦截的对象才执行Plugin.wrap方法,否者直接返回目标本省,这样可以减少目标被代理的次数。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-304540-1-1.html 上篇帖子: myBatis + hibernate 复习 下篇帖子: mybatis 配置文件报错
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表