Mybatis-Interceptor源码解析

Mybatis3.5.1源码分析

  1. Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源码解析
  2. Mybatis-Configuration源码解析
  3. Mybatis-事务对象源码解析
  4. Mybatis-数据源源码解析
  5. Mybatis缓存策略源码解析
  6. Mybatis-DatabaseIdProvider源码解析
  7. Mybatis-TypeHandler源码解析
  8. Mybatis-Reflector源码解析
  9. Mybatis-ObjectFactory,ObjectWrapperFactory源码分析
  10. Mybatis-Mapper各类标签封装类源码解析
  11. Mybatis-XMLMapperBuilder,XMLStatmentBuilder源码分析
  12. Mybatis-MapperAnnotationBuilder源码分析
  13. [Mybatis-MetaObject,MetaClass源码解析]https://www.jianshu.com/p/f51fa552f30a)
  14. Mybatis-LanguageDriver源码解析
  15. Mybatis-SqlSource源码解析
  16. Mybatis-SqlNode源码解析
  17. Mybatis-KeyGenerator源码解析
  18. Mybatis-Executor源码解析
  19. Mybatis-ParameterHandler源码解析
  20. Mybatis-StatementHandler源码解析
  21. Mybatis-DefaultResultSetHandler(一)源码解析
  22. Mybatis-DefaultResultSetHandler(二)源码解析
  23. Mybatis-ResultHandler,Cursor,RowBounds 源码分析
  24. Mybatis-MapperProxy源码解析
  25. Mybatis-SqlSession源码解析
  26. Mybatis-Interceptor源码解析

InterceptorChain

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.plugin;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 拦截器链
 * @author Clinton Begin
 */
public class InterceptorChain {

  /**
   * 拦截器集合
   */
  private final List<Interceptor> interceptors = new ArrayList<>();

  /**
   * 通知所有拦截器,通知所有拦截器对 {@code target} 进行个性化包装
   * @param target 目标对象
   * @return 经过所有拦截器进行个性化包装后的对象
   */
  public Object pluginAll(Object target) {
    //遍历所有拦截器
    for (Interceptor interceptor : interceptors) {
      //不断对target用拦截器包装再包装
      target = interceptor.plugin(target);
    }
    // 经过所有拦截器进行个性化包装后的对象
    return target;
  }

  /**
   * 添加拦截器
   * @param interceptor 拦截器
   */
  public void addInterceptor(Interceptor interceptor) {
    //将interceptor添加拦截器集合
    interceptors.add(interceptor);
  }

  /**
   * 返回拦截器集合
   * @return 不可修改的拦截器集合
   */
  public List<Interceptor> getInterceptors() {
    //Collections.unmodifiableList返回的对象其实是interceptors,但是这个返回对象修改会抛出异常
    return Collections.unmodifiableList(interceptors);
  }

}

Interceptor

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.plugin;

import java.util.Properties;

/**
 * 拦截器
 * 
 * @author Clinton Begin
 */
public interface Interceptor {

  /**
   * intercept:拦截
   * 拦截目标对象的目标方法的执行
   */
  Object intercept(Invocation invocation) throws Throwable;

  /**
   * 包装目标对象的:包装:为目标对象创建一个代理对象
   * @param target
   * @return
   */
  Object plugin(Object target);

  /**
   * setProperties:
   *      将插件注册时 的property属性设置进来
   */
  void setProperties(Properties properties);

}

拦截器的实现例子:

因为源码并没有比较好的示例,所以我上网找了一个
拦截器的实现例子

Plugin

/**
 *    Copyright 2009-2019 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.plugin;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.ibatis.reflection.ExceptionUtil;

/**
 * @author Clinton Begin
 */
public class Plugin implements InvocationHandler {

  /**
   * 目标对象
   */
  private final Object target;
  /**
   * 拦截器对象
   */
  private final Interceptor interceptor;
  /**
   * 目标类-方法集
   */
  private final Map<Class<?>, Set<Method>> signatureMap;

  /**
   *
   * @param target 目标对象
   * @param interceptor 拦截器
   * @param signatureMap 目标类-方法集
   */
  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }

  /**
   * 根据将 {@code interceptor} 的配置Intercepts注解信息 ,实现 {@code target} 的代理对象
   * 并返回出去,从而实现调用{@code interceptor}的intercept方法
   * @param target 目标对象
   * @param interceptor 拦截器
   * @return {@code target} 的代理对象,代理对象会回调Plugin的invoke方法,然后回调{@code interceptor}的intercept方法
   */
  public static Object wrap(Object target, Interceptor interceptor) {
    //将 interceptor 的配置Intercepts注解信息封装成存储拦截目标对象类,
    //以及拦截目标对象类的方法集映射关系的HashMap
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    //获取target的类型
    Class<?> type = target.getClass();
    //获取type 的在signatureMap有所包含的所有接口类
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    //如果interfaces不是空数组
    if (interfaces.length > 0) {
      //新建一个继承interfaces的代理对象
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

  /**
   * 代理方法
   * @param proxy 目标对象
   * @param method 方法对象
   * @param args 参数对象数组
   * @return 方法对象的返回对象
   * @throws Throwable
   */
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      //获取声明method的类的指定方法集
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      //如果方法集不为null且方法集包含method
      if (methods != null && methods.contains(method)) {
        //执行拦截器的intercept方法
        return interceptor.intercept(new Invocation(target, method, args));
      }
      //如果不是指定的方法,直接执行目标方法
      return method.invoke(target, args);
    } catch (Exception e) {
      //当e属于InvocationTargetException 和UndeclaredThrowableException的类型时,
      // 会从中 获取更加精确的异常抛出。
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

  /**
   * 将 {@code interceptor} 的配置Intercepts注解信息封装成存储拦截目标对象类,
   * 以及拦截目标对象类的方法集映射关系的HashMap
   * @param interceptor 拦截器
   * @return 一个存储拦截目标对象类,以及拦截目标对象类的方法集映射关系的HashMap
   */
  private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    //获取Intercepts注解
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    // issue #251
    //如果拦截器没有配置Intercepts注解
    if (interceptsAnnotation == null) {
      //抛出异常
      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
    }
    //获取Intercepts注解中的所有Signature注解
    Signature[] sigs = interceptsAnnotation.value();
    //新建一个HashMap,用于存储拦截目标对象类,以及拦截目标对象类的方法集
    Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
    //遍历Signature注解数组
    for (Signature sig : sigs) {
      //获取Signature注解中指定的拦截目标对象类对应的方法集
      Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
      try {
        //获取拦截目标对象类中对应Signature注解配置方法和参数类型的方法对象
        Method method = sig.type().getMethod(sig.method(), sig.args());
        //将方法对象添加到方法集里
        methods.add(method);
      } catch (NoSuchMethodException e) {
        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
      }
    }
    return signatureMap;
  }

  /**
   * 获取 {@code type} 的在 {@code signatureMap} 有所包含的所有接口类
   * @param type 目标对象类
   * @param signatureMap 存储拦截目标对象类,以及拦截目标对象类的方法集的HashMap
   * @return  {@code type} 的在 {@code signatureMap} 有所包含的接口类数组
   */
  private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
    //新建一个HashSet,用于保存type的在signatureMap包含的接口类
    Set<Class<?>> interfaces = new HashSet<>();
    //如果目标对象类不为null
    while (type != null) {
      //目标对象类的所有接口
      for (Class<?> c : type.getInterfaces()) {
        //如果signatureMap中包含这个接口类
        if (signatureMap.containsKey(c)) {
          //将接口类添加到interfaces
          interfaces.add(c);
        }
      }
      //取出type的父类重新赋值给type
      type = type.getSuperclass();
    }
    //将interfaces集和转换成接口类数组
    return interfaces.toArray(new Class<?>[interfaces.size()]);
  }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容