欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java MyBatis拦截器Inteceptor详细介绍

程序员文章站 2024-03-11 16:18:49
有许多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)。