mybatis源码分析(5)-----拦截器的实现原理(动态代理+责任链)

摘要:
上面用MyBatsi编写的拦截器模式是基于代理的模式。而myBatis的插件开发也以拦截器的形式集成到myBatis中。MyBatis的拦截程序已在org.apache中插入。ibatis在插件包下。MyBatis拦截器可以拦截的类:Executor、ParameterHandler、ResultSetHandler、StatementHandler。实现类BaseExecutor提供基本实现,在目标执行前后进行相关处理(模板),并提供抽象方法来实现特定操作。具体的实现类SimpleExecutor、BatchExecutor和ReuseExecutor专注于特定方法的实现。

写在前面

  MyBatsi 的拦截器模式是基于代理的代理模式。并且myBatis 的插件开发也是以拦截器的形式集成到myBatis 当中。

  MyBatis 的拦截器已经插件是在org.apache.ibatis.plugin包下面。

mybatis源码分析(5)-----拦截器的实现原理(动态代理+责任链)第1张

  MyBatis拦截器可以拦截的类,Executor(执行器),ParameterHandler(参数处理器),ResultSetHandler(结果集处理器),StatementHandler(句处理器)。

mybatis源码分析(5)-----拦截器的实现原理(动态代理+责任链)第2张

本文以Executor的插件为例子,来说明MyBatis 插件的工作原理

 1.首先分析 产生执行器Excutor 的代码

   通过interceptorChain的拦截器链,当一个执行器有过个拦截器,回产生拦截器链,也就是多重代理对象

   Configuration.java 文件
//产生执行器 public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; //这句再做一下保护,囧,防止粗心大意的人将defaultExecutorType设成null? executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; //然后就是简单的3个分支,产生3种执行器BatchExecutor/ReuseExecutor/SimpleExecutor 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); } //如果要求缓存,生成另一种CachingExecutor(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor if (cacheEnabled) { executor = new CachingExecutor(executor); } //此处调用插件,通过插件可以改变Executor行为 executor = (Executor) interceptorChain.pluginAll(executor); return executor; }

    Executor 执行器采用了模板方法模式。 

    Executor是执行器调度的核心。SqlSesson对外提供的api 其实就是 对于Executor 的调用(API-->SqlSesson-->Executor-->Statment-->DB)

  模板方法模式(解释):

     Executor 接口提供了query、update、commit、rollback 等方法。

     实现类BaseExecutor 给予基本的实现,在执行目标之前和之后 做了相关的处理(模板),且提供了实现具体操作的抽象方法。使得具体实现类SimpleExecutor、BatchExecutor、ReuseExecutor关注具体方法的实现。

     SimpleExecutor,BatchExecutor、ReuseExecutor 只需要实现自己的具体操作 doQuery、doUpdate、doCommit、doRollBack.

Executor executor = new SimpleExecutor();  
executor .query();

 2 其实Plugin采用了,插件,用的代理模式。自己实现了InvocationHandler接口,维护了目标类target和一个目标拦截器interceptor  

public class Plugin implements InvocationHandler {

  private Object target;
  private Interceptor interceptor;
  private Map<Class<?>, Set<Method>> signatureMap;

  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }

  public static Object wrap(Object target, Interceptor interceptor) {
    //取得签名Map
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    //取得要改变行为的类(ParameterHandler|ResultSetHandler|StatementHandler|Executor)
    Class<?> type = target.getClass();
    //取得接口
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    //产生代理
    if (interfaces.length > 0) {
      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)) {
        //调用Interceptor.intercept,也即插入了我们自己的逻辑
        return interceptor.intercept(new Invocation(target, method, args));
      }
      //最后还是执行原来逻辑
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }
}

myBatis 中用到的设计模式

一、建造者模式

BaseBuilder、XMLMapperBuilder、SQlSessionFactoryBuilder

二、工厂方法

SqlSessionFactory、TranscationFactory、MapperProxyFactory

三、模板方法模式

Executor、StatmentHandler

四、动态代理

Plugin、SqlSessionTemplate、MapperProxy

五、责任链模式

Interceptor、InterceptorChain

  

免责声明:文章转载自《mybatis源码分析(5)-----拦截器的实现原理(动态代理+责任链)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇SpringBoot打成jar后无法读取根路径和文件解决Lock之后 unlock 不了的问题下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

buildroot 制作Linux文件系统初级使用教程

buildroot 下载地址:https://buildroot.org/download.html 放在Linux文件下解压出来。 使用make menuconfig 进行配置相关的东西。 在使用这条命令之前,首先要安装相关的东西。 运行如下命令进行安装相关的库文件,我的是在Ubuntu14.04的环境下进行的。 sudo apt-get install...

mybatis sql查询慢

在mybatis为持久化的java框架中,mapper和xml文件映射的sql,有时在实际执行时会很慢,甚至一直查询不出来,调查发现原因有二: 1.参数化写法不同,执行逻辑不同。例如:#{param},${'param'} #符号标记的参数,在mybatis执行sql时,使用PreparedStatement对象,包含预编译sql操作,能防止sql注入安全...

# AMQP协议 0-9-1 简介

目录 AMQP是什么AMQP 0-9-1 模型简介 交换机和交换机类型 默认交换机 直连交换机 扇型交换机 主题交换机 头交换机 队列 队列名称 队列持久化 绑定 消费者 消息确认 拒绝消息 Negative Acknowledgements 预取消息 消息属性和有效载荷(消息主体) 消息确认 其他 AMQP 0-9-1 方法...

SqlServer数据库主从同步

分发/订阅模式实现SqlServer主从同步 在文章开始之前,我们先了解一下几个关键的概念: 分发服务器分发服务器是负责存储在同步过程中所用复制信息的服务器。可以比喻成报刊发行商。 分发数据库分发数据库用于存储发布数据库所做的更改。它还可以存储快照和合并发布的历史信息。存在于系统数据库中,默认为destribution. 发布服务器使服务器能够成为发布...

MyBatis(缓存机制)

缓存可以极大的提升查询效率。 MyBatis系统中默认定义两级缓存(一级缓存和二级缓存)。 一、两级缓存 1、一级缓存:(本地缓存):sqlSession级别的缓存。一级缓存是一直开启的;sqlSession级别的一个Map。   与数据库同一次会话期间查询到的数据会放在本地缓存中   以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;...

源码分析:若依用户 user_id查询返回除了用户表,为何还带有部门dept和角色role表呢

 通过认真分析SysUser.java 部份源码如下, private SysDept dept; 以下是toString(){ .append("dept", getDept()) .append("roles", getRoles()) } package com.ruoyi.common.core.domain.entity; imp...