为了不把开发和源码分析混淆,决定分开写;

接下来分析一下插件的源码,说道这里老套路先说一个设计模式,他就是责任链模式

责任链模式:就是把一件工作分别经过链上的各个节点,让这些节点依次处理这个工作,和装饰器模式不同,每个节点都知道后继者是谁,适合为完成同一个请求需要多个处理类的场景;

Handler:定义了一个处理请求的标准接口;

ConcreteHandler:具体的处理者,处理他负责的部分,根据业务可以结束处理流程,也可以将请求转发给他的后继者;

client:发送者,发起请求的客户端;

责任链模式优点:

1:降低耦合度,他将请求的发送者和接受者解耦;

2:简化了对象,使得对象不需要知道链的结构;

3:增强给对象指派责任的灵活性,通过改变链内的成员或者调动他们的次序,允许动态的新增或者删除责任;

4:增加新的请求处理类很方便;

Mybatis插件模块源码分析

1:插件的初始化(XMLConfigBuilder.pluginElement);

2:插件的加载(Configuration.new*方法,四大对象的创建);

3:插件的调用(Plugin.wrap,Plugin.invoke);

Mybatis插件理解

pluginElement(root.evalNode("plugins"));//从这行代码开始解析插件节点

 /**
* 解析所有的插件节点
* @param parent
* @throws Exception
*/
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
// 遍历所有的插件配置
for (XNode child : parent.getChildren()) {
// 获取插件的类名
String interceptor = child.getStringAttribute("interceptor");
// 获取插件的配置
Properties properties = child.getChildrenAsProperties();
// 实例化插件对象
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
// 设置插件的属性 所以在插件中才可以得到XML中配置的属性
interceptorInstance.setProperties(properties);
// 将插件添加到configuration中 底层采用list保存所有的插件并记录顺序
// InterceptorChain 存在的类
// private final List<Interceptor> interceptors = new ArrayList<>();
configuration.addInterceptor(interceptorInstance);
}
}
}

其实在这里也不是经典的责任链模式,就像tomcat的filter一样,他并不知道自己的下一个节点是谁,他的顺序是由配置决定的由上而下的;其实这样演变也是有好处的就是解开了下级节点和当前节点的耦合,完全由配置决定;

 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);
}
// 如果存在<cache>节点 那么通过装饰器CachingExecutor 包装原始的executor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 通过InterceptorChain遍历所有的插件为Executor增强,添加插件的功能
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
 public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
// 这里调用的就是实现interceptor接口实现的plugin方法 传入目标对象
target = interceptor.plugin(target);
}
return target;
}

接下来看一下实现类中的plugin方法,在接口中可以定义默认方法实现之后我们的实现类中已经不需要重写plugin方法 了我们看一下接口中的

 /**
*
* @param target 被拦截的对象,他的作用就是给拦截的对象生成一个代理对象
* @return
*/
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
 /**
* 静态方法,用于帮助Interceptor生成动态代理
* @param target
* @param interceptor
* @return
*/
public static Object wrap(Object target, Interceptor interceptor) {
// 解析Interceptor上标志的@Intercepts注解得到的Signature信息
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
// 获取目标对象的类型
Class<?> type = target.getClass();
// 获取目标对象实现的接口,拦截器可以拦截4大对象实现的接口
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 使用JDK动态代理
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}

至于Plugin.invoke就是调用的我们实现的Interceptor接口的intercept方法;

作者:彼岸舞

时间:2020\03\24

内容关于:Mybatis

本文部分来源于网络,只做技术分享,一概不负任何责任

Mybatis源码学习第七天(插件源码分析)的更多相关文章

  1. mybatis源码学习:一级缓存和二级缓存分析

    目录 零.一级缓存和二级缓存的流程 一级缓存总结 二级缓存总结 一.缓存接口Cache及其实现类 二.cache标签解析源码 三.CacheKey缓存项的key 四.二级缓存TransactionCa ...

  2. SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的

    系列文章目录和关于我 一丶什么是SpringBoot自动装配 SpringBoot通过SPI的机制,在我们程序员引入一些starter之后,扫描外部引用 jar 包中的META-INF/spring. ...

  3. Mybatis源码学习第七天(PageHelper分析)

    其实我本来是不打算写这个PageHelper的,但是后来想了想,还是写了吧!现在市场用Mybatis的产品分页应该差不多都是用PageHelper来实现的,毕竟Mybatis的分页rowbound.. ...

  4. 【mybatis源码学习】利用maven插件自动生成mybatis代码

    [一]在要生成代码的项目模块的pom.xml文件中添加maven插件 <!--mybatis代码生成器--> <plugin> <groupId>org.mybat ...

  5. Mybatis源码学习第七天(插件开发原理)

    插件概述: 插件是用来改变或者扩展mybatis的原有功能,mybatis的插件就是通过继承Interceptor拦截器实现的,在没有完全理解插件之前j禁止使用插件对mybatis进行扩展,有可能会导 ...

  6. Netty源码学习(七)FastThreadLocal

    0. FastThreadLocal简介 如同注释中所说:A special variant of ThreadLocal that yields higher access performance ...

  7. yii2源码学习笔记(七)

    今天继续了解model类 /** 2 * Returns the form name that this model class should use. 3 * 4 * 返回表单的名称,就是这个 mo ...

  8. 【js】vue 2.5.1 源码学习 (七) 初始化之 initState 响应式系统基本思路

    大体思路(六) 本节内容: 一.生命周期的钩子函数的实现 ==> callHook(vm , 'beforeCreate') beforeCreate 实例创建之后 事件数据还未创建 二.初始化 ...

  9. spring源码学习(三)--spring循环引用源码学习

    在spring中,是支持单实例bean的循环引用(循环依赖)的,循环依赖,简单而言,就是A类中注入了B类,B类中注入了A类,首先贴出我的代码示例 @Component public class Add ...

随机推荐

  1. MySQL回顾

    一. 对数据库的操作 1. 创建一个库 create database 库名 create database 库名 character set 编码 创建带有编码的 查看编码: 2. 删除一个库 dr ...

  2. ECS7天实践进阶训练营Day2:基于阿里云ECS部署MediaWiki

    一.概述 MediaWiki是全球最著名的开源Wiki程序,运行于PHP+MySQL环境,MediaWiki从2002年被作为维基百科的系统软件,并由大量其他应用实例(例如萌娘百科),因此MediaW ...

  3. linux系统中SSH免密设置报错

    执行 ssh-add ~/.ssh/msi_rsa 时报下面错误 Could not open a connection to your authentication agent. 解决办法: 执行命 ...

  4. MySQL连接查询、子查询与联合查询

    一 连接查询 1.交叉连接: 语法: from  表1  [cross]  join  表2  ; 例如: 表一: 表2: 交叉: 交叉查询并没有意义. 2.内连接: 语法: from  表1  [i ...

  5. 使用Python语言通过PyQt5和socket实现UDP服务器

    前言 最近做了一个小软件,记录一下相关内容. 已有条件 现在已有一个硬件设备作为客户端(暂称其为"电路"). 基于SIM卡,电路可以通过UDP协议传输数据(程序已经内置在电路中), ...

  6. C#LeetCode刷题之#39-组合总和(Combination Sum)

    目录 问题 示例 分析 问题 该文章已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3663 访问. 给定一个无重复元素的数组 candi ...

  7. C#LeetCode刷题之#728-自除数(Self Dividing Numbers)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3889 访问. 自除数 是指可以被它包含的每一位数除尽的数. 例如 ...

  8. 在java中,怎样创建编写javascript的环境?

    刚开始还没有学到这一块的时候,预习的时候也是在网上搜索这一类的信息时候, 可是都是八竿子碰不到边的!在此也是呕心沥血的为读者献上最好的: 1.首先:点击空白处>右键>project: 2. ...

  9. 记一次mysql数据库被勒索(上)

    家里搞了台旧电脑做NAS,安装了nextcloud,选择了mysql做为数据库. 当时也没有想太多,mysql数据库密码随便设置了个123456,用的一切正常. 然后,听说可以找电信申请换个公网IP的 ...

  10. PythonCrashCourse 第五章习题

    5.1编写一系列条件测试;将每个测试以及你对其结果的预测和实际结果都打印出来.你编写的代码应类似于下面这样: car = 'subaru' print("Is car == 'subaru' ...