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类中方法*/ publicParameterHandlernewParameterHandler(MappedStatementmappedStatement,ObjectparameterObject,BoundSqlboundSql){ ParameterHandlerparameterHandler=mappedStatement.getLang().createParameterHandler(mappedStatement,parameterObject,boundSql); /*拦截ParameterHandler*/ parameterHandler=(ParameterHandler)interceptorChain.pluginAll(parameterHandler); returnparameterHandler; } publicResultSetHandlernewResultSetHandler(Executorexecutor,MappedStatementmappedStatement,RowBoundsrowBounds,ParameterHandlerparameterHandler, ResultHandlerresultHandler,BoundSqlboundSql){ ResultSetHandlerresultSetHandler=newDefaultResultSetHandler(executor,mappedStatement,parameterHandler,resultHandler,boundSql,rowBounds); /*拦截ResultSetHandler*/ resultSetHandler=(ResultSetHandler)interceptorChain.pluginAll(resultSetHandler); returnresultSetHandler; } publicStatementHandlernewStatementHandler(Executorexecutor,MappedStatementmappedStatement,ObjectparameterObject,RowBoundsrowBounds,ResultHandlerresultHandler,BoundSqlboundSql){ StatementHandlerstatementHandler=newRoutingStatementHandler(executor,mappedStatement,parameterObject,rowBounds,resultHandler,boundSql); /*拦截StatementHandler*/ statementHandler=(StatementHandler)interceptorChain.pluginAll(statementHandler); returnstatementHandler; } publicExecutornewExecutor(Transactiontransaction,ExecutorTypeexecutorType){ executorType=executorType==null?defaultExecutorType:executorType; executorType=executorType==null?ExecutorType.SIMPLE:executorType; Executorexecutor; if(ExecutorType.BATCH==executorType){ executor=newBatchExecutor(this,transaction); }elseif(ExecutorType.REUSE==executorType){ executor=newReuseExecutor(this,transaction); }else{ executor=newSimpleExecutor(this,transaction); } if(cacheEnabled){ executor=newCachingExecutor(executor); } /*拦截Executor*/ executor=(Executor)interceptorChain.pluginAll(executor); returnexecutor; }
实现一个自定义拦截器只需实现Interceptor接口即可,大致代码如下:
代码如下
/*注解表明要拦截哪个接口的方法及其参数*/ @Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})}) publicclassYourInterceptorimplementsInterceptor{ publicObjectintercept(Invocationinvocation)throwsThrowable{ doSomeThing(); /*注:此处实际上使用Invocation.proceed()方法完成interceptorChain链的遍历调用(即执行所有注册的Interceptor的intercept方法),到最终被代理对象的原始方法调用*/ returninvocation.proceed(); } /*生成成对目标target的代理,而@Intercepts的注解是在Plugin.wrap中用到*/ @Override publicObjectplugin(Objecttarget){ /*当目标类是StatementHandler类型时,才包装目标类,不做无意义的代理*/ return(targetinstanceofStatementHandler)?Plugin.wrap(target,this):target; } /*用于设置自定义的拦截器配置参数*/ @Override publicvoidsetProperties(Propertiesproperties){ } }
其中,拦截调用的代码均在Plugin.wrap中:
代码如下
/*org.apache.ibatis.plugin.Plugin类*/ publicclassPluginimplementsInvocationHandler{ /*省略代码...*/ publicstaticObjectwrap(Objecttarget,Interceptorinterceptor){ /*此处即为获取Interceptor的注解签名*/ Map<Class<?>,Set<Method>>signatureMap=getSignatureMap(interceptor); Class<?>type=target.getClass(); /*获取拦截目标类相匹配的接口*/ Class<?>[]interfaces=getAllInterfaces(type,signatureMap); if(interfaces.length>0){ /*使用jdk动态代理*/ returnProxy.newProxyInstance(type.getClassLoader(),interfaces,newPlugin(target,interceptor,signatureMap)); } returntarget; } /*拦截目标类的所有方法的执行都会变为在此执行*/ @Override publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{ try{ Set<Method>methods=signatureMap.get(method.getDeclaringClass()); if(methods!=null&&methods.contains(method)){ /*执行拦截器方法*/ returninterceptor.intercept(newInvocation(target,method,args)); } returnmethod.invoke(target,args); }catch(Exceptione){ throwExceptionUtil.unwrapThrowable(e); } } /*省略代码...*/ }
可以看到MyBatis的拦截器设计核心代码还是比较简单的,但是足够灵活。实际使用时注意,不做无意义的代理(Plugin.wrap)。