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

[经验分享] mybatis的拦截器interceptor源代码解析

[复制链接]

尚未签到

发表于 2016-11-27 11:33:29 | 显示全部楼层 |阅读模式
  
mybatis支持拦截器,实现的原理就是利用JDK的动态代理。先前利用拦截器实现了分页功能,如今又利用拦截器实现日志记录的功能,感觉拦截器还是不错的,只是相对于spring3 mvc的拦截器感觉有些逊色。接下来,我着重分析一些拦截的使用。


【基本思路】拦截器在哪里拦截?什么情况下才会拦截代理?怎么代理呢?只要搞清楚这些,基本的拦截器功能也就了如指掌啦。


拦截器在哪里呢?mybatis到底提供几处可以拦截呢?请看下图,通过分析源码可知基本查询流程如下:


DSC0000.jpg




【简洁的流程】 首先mybatis初始化加载的时候,利用
MapperProxy代理
自己的Mapper接口类,
生成一个代理处理类,代理处理的逻辑都在
invoke方法里,首先获取到目标类的接口
declaringInterface,然后生成
MapperMethod,(其中
sqlSession是
SqlSessionTemplate,是spring连接mybatis的模板类,是由spring负责生成的),生成了
MapperMethod,直接调用
MapperMethod可执行的方法


execute就能获取执行结果,整个流程非常简洁,非常和spring3 mvc的servlet实现类似,也就是说,
MapperMethod就是一个命令,执行命令获取执行结果,也就是标准的命令模式。整个流程代码如下:



public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
final Class<?> declaringInterface = findDeclaringInterface(proxy, method);
final MapperMethod mapperMethod = new MapperMethod(declaringInterface, method, sqlSession);
final Object result = mapperMethod.execute(args);
if (result == null && method.getReturnType().isPrimitive()
&& !method.getReturnType().equals(Void.TYPE)) {
throw new BindingException(
"Mapper method '" + method.getName() + "' (" + method.getDeclaringClass()
+ ") attempted to return null from a method with a primitive return type ("
+ method.getReturnType() + ").");
}
return result;
}
  









【分步分析】







  • MapperMethod




查看excute方法可知,就是根据不同的sql类型,交给sqlSession,也就是SqlSessionTemplate类的对象,主要是完成获取执行参数


param






commandName,执行具体的操作,例如,




result =

 

sqlSession

.
selectList
(

commandName

, param);





其中sql类型的获取可参考初始化的时候加载的setupCommandType(),初始化的时候还包括获取map的sqlID等




  • SqlSessionTemplate和


    SqlSessionInterceptor



利用eclipse的f4一下,可知


SqlSessionTemplate的实际执行是交给它的代理类完成的。查看




SqlSessionTemplate构造函数可知,是由




SqlSessionInterceptor动态代理的,那么所有的处理逻辑都是在




invoke方法里,从中可知,就是获取SqlSession,执行操作。其中




SqlSessionUtils
.
getSqlSession静态方法调用外部类


SqlSessionTemplate







SqlSessionFactory,




ExecutorType,这些都是在mybatis加载




SqlSessionTemplate




构造函数初始化的。




  • DefaultSqlSession



这个就是从




DefaultSqlSessionFactory工厂生成获取的,具体的加载流程就是在




sqlSession

.
selectList静态方法里。



接着上面的查询流程可知,调用的




selectList方法,通过




MapperMethod方法中获取的




commandName,获取到




MappedStatement对象,主要负责的就是sql里xml信息等,具体执行交给




executor,




executor是由




SqlSessionTemplate的




ExecutorType决定的。






  • executor和


    Plugin



executor的获取是


DefaultSqlSessionFactory生成




DefaultSqlSession,加载的方法是在




Configuration里




newExecutor从中可知




cacheEnabled默认为ture,追加缓存实现,具体的实现还是




SimpleExecutor,属于静态代理,




cacheEnabled只是




实现了缓存功能。




executor = (Executor)

 

interceptorChain

.pluginAll(executor);



表明可追加拦截器。查看代码可知,


 


  public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}  
 


就是调用拦截Interceptor的方法plugin,我们在plugin方法里一般都需要调用Plugin的静态方法wrap生成Plugin动态代理目标类,由于又是动态代理,实现逻辑都是在invoke方法里,查看可知,调用Interceptor的intercept方法,由Invoction封装目标类,调用方法,调用参数。因此,我们实现自己的拦截器的时候,都可从intercept方法里获取到拦截的对象,方法,参数等。这里其中涉及到两个问题就是拦截器什么情况下才会代理的,怎样代理呢。第一个问题,查看wrap方法可知,就是查看Map里查找是否设定代理啦,以代理对象的class做为key,代理方法的set集合作为value,就能判定是否要代理的。Map里的数据是遍历拦截器的注解Intercepts,实现很简单。怎样打理的,就是调用Interceptor的intercept方法,具体的实现都是在自定义的拦截器方法里。






  • StatementHandler,


    ParameterHandler, 


    ResultSetHandler和


    Plugin






SimpleExecutor的
doQuery方法是具体的实现,

获取StatementHandler,然后通过StatementHandler获取Statement,其中



StatementHandler的获取又是在Configuration里newStatementHandler,同executor实现的类似,也是有RoutingStatementHandler代理实际的StatementHandler实现,通过StatementType获取具体的StatementHandler类,从默认配置可知,PreparedStatementHandler是实际代理的对象。这里也有


executor = (Executor) 



interceptorChain


.pluginAll(executor)可知



同样可添加拦截器。其中构造StatementHandler的时候,查看BaseStatementHandler构造函数可知,ParameterHandler和ResultSetHandler也是有Configuration生成的,同样也可追加拦截器。




  • Statement和日志代理




SimpleExecutor的
doQuery方法调用


prepareStatement


可知,


Statement获取是通过StatementHandler的prepare方法生成的,由于PreparedStatementHandler是具体的实现类,查看可知,instantiateStatement方法里是具体的获取Statement方法,由ConnectionLogger代理类调用PreparedStatementLogger.newInstance生成PreparedStatementLogger。其中ConnectionLogger是DefaultSqlSessionFactory里wrapConnection方法获取的。



【总结】


mybatis的拦截器都是在


Configuration类的




InterceptorChain负责调用位置,Plugin类负责生成代理,调用具体的拦截器实现类














  

运维网声明 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-306159-1-1.html 上篇帖子: Mybatis源码研究5:数据源的实现 下篇帖子: Spring整合mybatis整合的两种映射器细节
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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