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)。