@

如果没有自定义过拦截器, 可以看我前面的文章。如果不知道 JDK 动态代理怎么使用的, 可以看我这文章。 责任链设计模式理解起来很简单, 网上找个例子看看即可。

mybatis 插件的原理使用的是动态代理和责任链来实现的。

1 拦截哪些方法

前面说过, 可以通过注解 InteceptsSignature 来进行指定拦截哪些方法。 然而, 并不是说所有的方法都可以拦截的。

mybatis 拦截器所拦截的方法, 有如下类型:

  1. 1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  2. 2. ParameterHandler (getParameterObject, setParameters)
  3. 3. ResultSetHandler (handleResultSets, handleOutputParameters)
  4. 4. StatementHandler (prepare, parameterize, batch, update, query)

为什么说可以拦截的方法是这些呢?

mybatis 中, 以上几个类是 SqlSession 的四大对象。 SqlSession 通过这些对象实现对数据库的操作, 结果的处理。 因此, 从流程上来说, 是拦截这几个对象中的方法是有非常重要的作用的。



而在源码上的体现呢, 就在 Configuration 类中, 这个类的重要性不做过多的阐述, 可以看看前面的文章。

在总 xml 解析成 Configuration 过程中, 需要 new 出以上的几个类。而以上的几个类在后面都会调用 interceptorChain#pluginAll 方法。

2 如何代理

  1. public class InterceptorChain {
  2. /**
  3. * 拦截器列表
  4. */
  5. private final List<Interceptor> interceptors = new ArrayList<>();
  6. public Object pluginAll(Object target) {
  7. for (Interceptor interceptor : interceptors) {
  8. target = interceptor.plugin(target);
  9. }
  10. return target;
  11. }
  12. /**
  13. * 添加拦截器
  14. *
  15. * @param interceptor
  16. */
  17. public void addInterceptor(Interceptor interceptor) {
  18. interceptors.add(interceptor);
  19. }
  20. /**
  21. * 获取拦截器列表
  22. *
  23. * @return
  24. */
  25. public List<Interceptor> getInterceptors() {
  26. return Collections.unmodifiableList(interceptors);
  27. }
  28. }

可以看到, 其会将调用所有拦截器的 plugin 方法, 层层代理之后返回最终的代理对象。 要注意这里的层层代理。

如果有 A、B、C 三个拦截器(签名相同), 则在此时, 会被层层封装。 最后执行的时候, 是 A>B>C> target.proceed() >C>B>A.

3 代理对象

InterceptorChain 会调用每个拦截器中的 plugin 方法。该方法是会返回相应的代理对象的。

  1. /**
  2. * 拦截器接口
  3. *
  4. * @author Clinton Begin
  5. */
  6. public interface Interceptor {
  7. /**
  8. * 执行拦截逻辑的方法
  9. *
  10. * @param invocation 调用信息
  11. * @return 调用结果
  12. * @throws Throwable 异常
  13. */
  14. Object intercept(Invocation invocation) throws Throwable;
  15. /**
  16. * 代理
  17. *
  18. * @param target
  19. * @return
  20. */
  21. Object plugin(Object target);
  22. /**
  23. * 根据配置来初始化 Interceptor 方法
  24. * @param properties
  25. */
  26. void setProperties(Properties properties);
  27. }

其中的 plugin 是需要我们来实现的。 而 mybatis 也给我们提供了很方便的方法。

  1. public static Object wrap(Object target, Interceptor interceptor) {
  2. // 获取类型及对应的方法信息
  3. Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
  4. Class<?> type = target.getClass();
  5. // 获取所有需要拦截的接口
  6. Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
  7. if (interfaces.length > 0) {
  8. // 创建代理对象
  9. return Proxy.newProxyInstance(
  10. type.getClassLoader(),
  11. interfaces,
  12. new Plugin(target, interceptor, signatureMap));
  13. }
  14. return target;
  15. }

我们在重写 plugin 方法时, 只需要调用上面这个方法即可。 其会返回 Plugin 这个类的一个对象。

  1. public class Plugin implements InvocationHandler
  1. @Override
  2. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  3. try {
  4. // 获取方法所在类中, 可以被拦截的所有的方法
  5. Set<Method> methods = signatureMap.get(method.getDeclaringClass());
  6. // 如果需要被拦截, 则调用 interceptor.intercept
  7. if (methods != null && methods.contains(method)) {
  8. return interceptor.intercept(new Invocation(target, method, args));
  9. }
  10. // 没有被拦截则正常调用
  11. return method.invoke(target, args);
  12. } catch (Exception e) {
  13. throw ExceptionUtil.unwrapThrowable(e);
  14. }
  15. }

由于 JDK 的动态代理是接口级别的。 因此, 其代理了类的所有接口的方法。 然而并不是所有的方法都是需要被代理的, 因此, 在方法中通过注解中的签名信息进行区分。

4 责任链设计模式

在插件的使用过程中, 责任链设计模式体现在动态代理的层层嵌套的代理增强之中。 体现在interceptorChain#pluginAll 方法中。 调用时会层层的进行代理。 mybatis 插件的原理-责任链和动态代理的体现

mybatis 插件的原理-责任链和动态代理的体现的更多相关文章

  1. 【Mybtais】Mybatis 插件 Plugin开发(一)动态代理步步解析

    需求: 对原有系统中的方法进行'拦截',在方法执行的前后添加新的处理逻辑. 分析: 不是办法的办法就是,对原有的每个方法进行修改,添加上新的逻辑:如果需要拦截的方法比较少,选择此方法到是会节省成本.但 ...

  2. 小白也能看懂插件化DroidPlugin原理(一)-- 动态代理

    前言:插件化在Android开发中的优点不言而喻,也有很多文章介绍插件化的优势,所以在此不再赘述.前一阵子在项目中用到 DroidPlugin 插件框架 ,近期准备投入生产环境时出现了一些小问题,所以 ...

  3. 小白也能看懂的插件化DroidPlugin原理(一)-- 动态代理

    前言:插件化在Android开发中的优点不言而喻,也有很多文章介绍插件化的优势,所以在此不再赘述.前一阵子在项目中用到 DroidPlugin 插件框架 ,近期准备投入生产环境时出现了一些小问题,所以 ...

  4. Mybatis中原生DAO实现和Mapper动态代理实现

    Mybatis开发dao的方法通常用两种,一种是传统DAO的方法,另一种是基于mapper代理的方法. 一.传统DAO方式开发 1.sql语句映射文件编写 User.xml <?xml vers ...

  5. 送命题:讲一讲Mybatis插件的原理及如何实现?

    持续原创输出,点击上方蓝字关注我吧 目录 前言 环境配置 什么是插件? 如何自定义插件? 举个栗子 用到哪些注解? 如何注入Mybatis? 测试 插件原理分析 如何生成代理对象? 如何执行? 总结 ...

  6. Mybatis九( mybatis插件的原理及使用)

    1.插件执行原理 一.demo 1.测试类 @Test public void test1() { String resource = "mybatis-config.xml"; ...

  7. mybatis插件机制原理

    mybatis插件机制及分页插件原理 参考链接:mybatis插件机制及分页插件原理 如何编写一个自定义mybatis插件 参考链接:mybatis 自定义插件的使用

  8. 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)

    代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...

  9. 初看Mybatis 源码 (二) Java动态代理类

    先抛出一个问题,用过Mybatis的都知道,我们只需要定义一个Dao的接口,在里面写上一些CRUD相关操作,然后配置一下sql映射文件,就可以达到调用接口中的方法,然后执行sql语句的效果,为什么呢? ...

随机推荐

  1. Spring Cloud Zuul的动态路由怎样做?集成Nacos实现很简单

    一.说明 网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的:本文主要介绍实现的思路,并且以Na ...

  2. rabbit - producer的confirm和consumer的ack模式

    本篇和大家分享的是关于rabbit的生产和消费方的一些实用的操作:正如文章标题,主要内容如producer的confirm和consumer的ack,这两者使用的模式都是用来保证数据完整性,防止数据丢 ...

  3. Spring框架入门之AOP

    Spring框架入门之AOP 一.Spring AOP简单介绍 AOP AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented ...

  4. MSIL实用指南-方法的调用

    方法调用指令主要有Call和Callvirt. 调用static或sealed修饰的方法,用Call指令. 调用virtual或abstract修饰的方法,用Callvirt指令. 代码实例: ilG ...

  5. Git 忽略某些文件提交

    在项目中有些配置文件不需要提交,但是有同学在后面开发中发现在.igonore文件中无论如何都无法忽略某些文件的提交.原因在这里: 已经维护起来的文件,即使加上了gitignore,也无济于事.---- ...

  6. cogs 1199选课(树形dp 背包或多叉转二叉

    http://cogs.pro:8080/cogs/problem/problem.php?pid=vQyiJkkPP 题意:给m门课,每门课在上完其先修课后才能上,要你从中选n门课使得总学分尽可能大 ...

  7. 天梯杯 L2-005. 集合相似度

    L2-005. 集合相似度 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 给定两个整数集合,它们的相似度定义为:Nc/Nt*1 ...

  8. CodeForces Round#480 div3 第2场

    这次div3比上次多一道, 也加了半小时, 说区分不出1600以上的水平.(我也不清楚). A. Remove Duplicates 题意:给你一个数组,删除这个数组中相同的元素, 并且保留右边的元素 ...

  9. codeforces 761 C. Dasha and Password(多维dp)

    题目链接:http://codeforces.com/contest/761/problem/C 题意:给出n行的字符串每一列都从第一个元素开始可以左右移动每一行字符串都是首位相连的. 最后问最少移动 ...

  10. 【Nginx】实现负载均衡的几种方式

    要理解负载均衡,必须先搞清楚正向代理和反向代理. 注: 正向代理,代理的是用户. 反向代理,代理的是服务器 什么是负载均衡 当一台服务器的单位时间内的访问量越大时,服务器压力就越大,大到超过自身承受能 ...