java MyBatis拦截器Inteceptor详细介绍
程序员文章站
2024-03-12 13:44:02
有许多java初学者对于mybatis拦截器inteceptor不是很了解,在这里我来为各位整理下篇关于java中mybatis拦截器inteceptor详解,
本文主要...
有许多java初学者对于mybatis拦截器inteceptor不是很了解,在这里我来为各位整理下篇关于java中mybatis拦截器inteceptor详解,
本文主要分析mybatis的插件机制,实际就是java动态代理实现的责任链模式实现。
根据官方文档。mybatis只允许拦截以下方法,这个决定写拦截器注解签名参数。
代码如下
executor (update, query, flushstatements, commit, rollback, gettransaction, close, isclosed) parameterhandler (getparameterobject, setparameters) resultsethandler (handleresultsets, handleoutputparameters) statementhandler (prepare, parameterize, batch, update, query)
拦截处理的源码如下,其中interceptorchain.pluginall(..)即为织入自定义拦截器:
代码如下
/* org.apache.ibatis.session.configuration类中方法 */ public parameterhandler newparameterhandler(mappedstatement mappedstatement, object parameterobject, boundsql boundsql) { parameterhandler parameterhandler = mappedstatement.getlang().createparameterhandler(mappedstatement, parameterobject, boundsql); /* 拦截parameterhandler*/ parameterhandler = (parameterhandler) interceptorchain.pluginall(parameterhandler); return parameterhandler; } public resultsethandler newresultsethandler(executor executor, mappedstatement mappedstatement, rowbounds rowbounds, parameterhandler parameterhandler, resulthandler resulthandler, boundsql boundsql) { resultsethandler resultsethandler = new defaultresultsethandler(executor, mappedstatement, parameterhandler, resulthandler, boundsql, rowbounds); /* 拦截resultsethandler*/ resultsethandler = (resultsethandler) interceptorchain.pluginall(resultsethandler); return resultsethandler; } public statementhandler newstatementhandler(executor executor, mappedstatement mappedstatement, object parameterobject, rowbounds rowbounds, resulthandler resulthandler, boundsql boundsql) { statementhandler statementhandler = new routingstatementhandler(executor, mappedstatement, parameterobject, rowbounds, resulthandler, boundsql); /* 拦截statementhandler*/ statementhandler = (statementhandler) interceptorchain.pluginall(statementhandler); return statementhandler; } public executor newexecutor(transaction transaction, executortype executortype) { executortype = executortype == null ? defaultexecutortype : executortype; executortype = executortype == null ? executortype.simple : executortype; executor executor; if (executortype.batch == executortype) { executor = new batchexecutor(this, transaction); } else if (executortype.reuse == executortype) { executor = new reuseexecutor(this, transaction); } else { executor = new simpleexecutor(this, transaction); } if (cacheenabled) { executor = new cachingexecutor(executor); } /* 拦截executor*/ executor = (executor) interceptorchain.pluginall(executor); return executor; }
实现一个自定义拦截器只需实现interceptor接口即可,大致代码如下:
代码如下
/* 注解表明要拦截哪个接口的方法及其参数 */ @intercepts({ @signature(type = statementhandler.class, method = "prepare", args = { connection.class }) }) public class yourinterceptor implements interceptor{ public object intercept(invocation invocation) throws throwable{ dosomething(); /* 注:此处实际上使用invocation.proceed()方法完成interceptorchain链的遍历调用(即执行所有注册的interceptor的intercept方法),到最终被代理对象的原始方法调用 */ return invocation.proceed(); } /*生成成对目标target的代理,而@intercepts的注解是在plugin.wrap中用到*/ @override public object plugin(object target){ /* 当目标类是statementhandler类型时,才包装目标类,不做无意义的代理 */ return (target instanceof statementhandler)?plugin.wrap(target, this):target; } /*用于设置自定义的拦截器配置参数*/ @override public void setproperties(properties properties){ } }
其中,拦截调用的代码均在plugin.wrap中:
代码如下
/* org.apache.ibatis.plugin.plugin类 */ public class plugin implements invocationhandler { /* 省略代码... */ public static object wrap(object target, interceptor interceptor) { /* 此处即为获取interceptor的注解签名 */ map<class<?>, set<method>> signaturemap = getsignaturemap(interceptor); class<?> type = target.getclass(); /* 获取拦截目标类相匹配的接口 */ class<?>[] interfaces = getallinterfaces(type, signaturemap); if (interfaces.length > 0) { /* 使用jdk动态代理 */ return proxy.newproxyinstance(type.getclassloader(), interfaces, new plugin(target, interceptor, signaturemap)); } return target; } /* 拦截目标类的所有方法的执行都会变为在此执行 */ @override 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)) { /* 执行拦截器方法 */ return interceptor.intercept(new invocation(target, method, args)); } return method.invoke(target, args); } catch (exception e) { throw exceptionutil.unwrapthrowable(e); } } /* 省略代码... */ }
可以看到mybatis的拦截器设计核心代码还是比较简单的,但是足够灵活。实际使用时注意,不做无意义的代理(plugin.wrap)。