gball个人知识库
首页
基础组件
基础知识
算法&设计模式
  • 操作手册
  • 数据库
  • 极客时间
  • 每日随笔
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 画图工具 (opens new window)
关于
  • 网盘 (opens new window)
  • 分类
  • 标签
  • 归档
项目
GitHub (opens new window)

ggball

后端界的小学生
首页
基础组件
基础知识
算法&设计模式
  • 操作手册
  • 数据库
  • 极客时间
  • 每日随笔
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 画图工具 (opens new window)
关于
  • 网盘 (opens new window)
  • 分类
  • 标签
  • 归档
项目
GitHub (opens new window)
  • 面试

  • 数据库

  • linux

  • node

  • tensorFlow

  • 基础组件

    • mybatis

      • mybatis概览
      • mybatis-执行器
      • mybatis的mapper执行过程
      • mybatis核心组件
      • mybatis缓存解析
      • mybatis日志体系
      • mybatis拦截器执行原理
        • 1、使用mybatis的拦截器
          • 1.创建拦截器
          • 2.mybatis配置文件配置插件
        • 2、mybatis的拦截器如何创建的
        • 3、mybatis的拦截器在哪些时机会被使用到
          • 添加拦截方法
          • 拦截器被使用到的过程
        • 4、总结
    • spring

    • 消息队列

    • springboot

    • tomcat如何工作

    • elasticsearch

  • 基础知识

  • 算法与设计模式

  • 分布式

  • 疑难杂症

  • go学习之旅

  • 极客时间

  • 知识库
  • 基础组件
  • mybatis
ggball
2021-12-06

mybatis拦截器执行原理

mybatis的拦截器本人平时也很少用到,没了解之前,也只是知道运用到了动态代理用来增强方法的功能,但是不了解其中的原理。为了更好的使用mybatis,这次,我记录下我所了解的mybatis的原理,本文不一定完全正确,可能有理解不到位的地方。

# 1、使用mybatis的拦截器

像平常使用mybatis框架时,如果哪句sql报错了,我们可以通过控制台或日志打印的sql去查看sql的问题,但是如果sql有太多的参数,其实是很不方便的,自己还得手动去把一个一个参数给设置上,有些浪费时间,这时候就可以利用mybatis的拦截器去帮我们把参数给设置上。

配置步骤

# 1.创建拦截器

@Intercepts({
        @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
        @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
        @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})
})
public class SlowSqlInterceptor implements Interceptor {


    @Override
    public Object intercept(Invocation invocation) throws InvocationTargetException, IllegalAccessException {
       // 主要逻辑 拼接参数到sql,并打印
    }

    @Override
    public Object plugin(Object target) {
        // 创建代理对象
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
      	// 设置属性
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
 * @author Clinton Begin
 */
public class Invocation {

  // 目标对象,即ParameterHandler、ResultSetHandler、StatementHandler或者Executor实例
  private final Object target;
  // 目标方法,即拦截的方法
  private final Method method;
  // 目标方法参数
  private final Object[] args;

  public Invocation(Object target, Method method, Object[] args) {
    this.target = target;
    this.method = method;
    this.args = args;
  }

  public Object getTarget() {
    return target;
  }

  public Method getMethod() {
    return method;
  }

  public Object[] getArgs() {
    return args;
  }

  /**
   * 执行目标方法
   * @return 目标方法执行结果
   * @throws InvocationTargetException
   * @throws IllegalAccessException
   */
  public Object proceed() throws InvocationTargetException, IllegalAccessException {
    return method.invoke(target, args);
  }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

拦截器类实现intercepter接口

  • intercepter方法主要写拦截方法的逻辑,Invocation对象主要有三个内置对象和proceed方法,proceed方法的作用就是用来执行代理对象的方法,对象target是被代理的对象实例,对象method是拦截方法,对象args是被调用方法传入的参数,很符合调用代理对象invoke方法的条件。

  • plugin方法接受实际对象,作用返回一个代理对象,这里是调用了Plugin提供的warp方法,方便创建代理对象,==我们也可以自己写创建代理对象的代码==。

  • setProperties方法设置属性,当拦截器被扫描到时,会调用此方法。

  • 类上还需要打上@Intercepts注解,value值为Signature对象,作用是拦截方法作用到哪个mybatis对象的哪个方法上,像图上是当调用statementHandler的query,update,batch方法时,mybatis才会进行拦截

# 2.mybatis配置文件配置插件

image-20211204171440931

# 2、mybatis的拦截器如何创建的

image-20211206122358988

image-20211206122417844

  1. configuration创建时,会去扫描配置文件的<plugin>标签
  2. 获取<plugin>标签的interceptor属性
  3. 获取拦截器属性,转换为Properties对象
  4. 创建拦截器实例 利用TypeAliasRegistry的resolveAlias方法,将传进来的别名,判断如果别名在TYPE_ALIASES里面,则直接获取类对象,如果不是则反射获取类对象
  5. 设置拦截器实例属性信息 将第三步的properties属性添加到拦截器实例里面
  6. 將拦截器实例添加到拦截器链中 (拦截器在configuration里面)

# 3、mybatis的拦截器在哪些时机会被使用到

在Configuration类的

newParameterHandler()、newResultSetHandler()、newStatementHandler()、newExecutor()

这些工厂方法中,都调用了InterceptorChain对象的pluginAll()方法。-

image-20211206140008644

image-20211206140028014

image-20211206140042350

image-20211206135945837

image-20211206100432298

  /**
   * 该方法用于创建Executor、ParameterHandler、ResultSetHandler、StatementHandler的代理对象
   Plugin.warp()方法首先获取自定义的拦截类上的@Signature注解上的信息并存入map,那就知道了要拦截哪些对象地哪些方法,然后判断传入的target对象是否满足拦截对象的类型,满足则创建代理对象,不满足则直接返回原对象。
   * @param target
   * @param interceptor
   * @return
   */
  public static Object wrap(Object target, Interceptor interceptor) {
    // 调用getSignatureMap()方法获取自定义插件中,通过Intercepts注解指定的方法
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    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;
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 添加拦截方法

这里拿在执行器上添加拦截方法举例:

1. mybatis在创建sqlSession时,会创建执行器Executor,同时调用拦截器的pluginAll方法,调用每个拦截器的plugin方法,这个方法主要是创建代理对象,将代理功能增强到被拦截的方法上。
2. Plugin.warp()方法首先获取自定义的拦截类上的@Signature注解上的信息并存入map,那就知道了要拦截哪些对象地哪些方法,然后判断传入的target对象是否满足拦截对象的类型,满足则创建代理对象,不满足则直接返回原对象。

# 拦截器被使用到的过程

image-20211206155858474

image-20211206160023347

image-20211206160054776

image-20211206160144051

image-20211206160202063

image-20211206160249200

image-20211206160309014

image-20211206160510749

1. mapper执行方法时,相当于调用代理对象的invoke方法,mapperMethod会调用sqlSession的的select或者update方法
2. sqlSession实际调用的是执行器的query或者update方法,如果不走缓存的话,最后会走到SimpleExecutor的doQuery方法
3. configuration创建statementHandler代理对象,同样有关于statementHandler的拦截器,也会创建代理类
4. statementHandler执行query方法,如果statementHandler对象是代理对象,则进入Plugin的invoke方法,如果当前执行的方法符合被拦截的方法的要求,那么就会执行拦截方法,否则不执行拦截方法,如果statementHandler对象不是代理对象,直接执行原方法。

# 4、总结

原来以为拦截器只是简单的使用下动态代理,看了mybatis的拦截器发现,一个经得起捶打的功能是不可能那么简单的,里面用到了动态代理解决mapper的实现问题,适配器模式用来解决结果对象,参数对象的映射,而且在我看来configuration类做了太多的工作,很多初始化的数据都能在里面找到,只要持有configuration对象,很多数据都可以直接拿到,避免现拿现查的麻烦。

上次更新: 2025/06/04, 15:06:15
mybatis日志体系
spring管理bean之间的协同方式

← mybatis日志体系 spring管理bean之间的协同方式→

最近更新
01
AIIDE
03-07
02
githubActionCICD实战
03-07
03
windows安装Deep-Live-Cam教程
08-11
更多文章>
Theme by Vdoing
总访问量 次 | 总访客数 人
| Copyright © 2021-2025 ggball | 赣ICP备2021008769号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×

评论

  • 评论 ssss
  • 回复
  • 评论 ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
  • 回复
  • 评论 ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
  • 回复
×