Young's blog Young's blog
首页
Spring
  • 前端文章1

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Young

首页
Spring
  • 前端文章1

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • JDBC简单的示例
  • MyBatis 架构与原理到执行流程
  • MyBatis的插件开发运行原理
  • 为什么 MyBatis 源码中,没有我那种 if···else
  • MyBatis 执行流程及源码解析
  • MyBatis
andanyang
2023-06-29

MyBatis的插件开发运行原理

# MyBatis 的插件运行原理

MyBatis 插件的运行是基于 JDK 动态代理 + 拦截器链实现。

MyBatis 的拦截器可以拦截 4 大对象,开发者通过自定义拦截器,拦截对应的方法实现插件。

  • Executor(update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler(getParameterObject, setParameters)
  • ResultSetHandler(handleResultSets, handleOutputParameters)
  • StatementHandler(prepare, parameterize, batch, update, query)

在 MyBatis 的全局配置类 Configuration 中,定义了一个拦截器链。InterceptorChain 中用一个 List 集合保存所有的拦截器,其中 pluginAll 方法会遍历 List 集合,并调用所有拦截器的 plugin 方法。

image-20230629090103581

public class InterceptorChain {
    // 定义了一个List集合装拦截器对象
    private final List<Interceptor> interceptors = new ArrayList();

    public InterceptorChain() {
    }

    // 遍历interceptors,并调用拦截器的plugin方法
    public Object pluginAll(Object target) {
        Interceptor interceptor;
        for(Iterator var2 = this.interceptors.iterator(); var2.hasNext(); target = interceptor.plugin(target)) {
            interceptor = (Interceptor)var2.next();
        }

        return target;
    }

    // 添加拦截器
    public void addInterceptor(Interceptor interceptor) {
        this.interceptors.add(interceptor);
    }

    // 获取interceptors
    public List<Interceptor> getInterceptors() {
        return Collections.unmodifiableList(this.interceptors);
    }
}
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

MyBatis 的拦截器,自定义插件就需要实现该接口

public interface Interceptor {
    // 拦截处理
    Object intercept(Invocation var1) throws Throwable;

    // 生成代理对象
    default Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    // 插件的属性设置
    default void setProperties(Properties properties) {
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Invocation {
    // 目标对象
    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 this.target;
    }

    public Method getMethod() {
        return this.method;
    }

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

    // 继续往下执行
    public Object proceed() throws InvocationTargetException, IllegalAccessException {
        return this.method.invoke(this.target, this.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

# MyBatis 插件的开发

第一步:实现  Interceptor 接口

package com.learn.blog.demo;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.lang.reflect.Field;
import java.util.Properties;

/**
 * 自定义 MyBatis 插件,拦截 Executor 对象的 query 方法,添加limit 1
 */
@Intercepts({
        @Signature(
                type = Executor.class,
                method = "query",
                args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class MyBatisPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 方法的参数
        Object[] args = invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement) args[0];
        Object parameterObject = args[1];
        BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);
        // 获取到原查询sql语句
        String sql = boundSql.getSql();

        // 利用反射修改boundSql的字段
        Field field = boundSql.getClass().getDeclaredField("sql");
        field.setAccessible(true);
        field.set(boundSql, sql + "limit 1");

        // 修改完继续执行
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Interceptor.super.plugin(target);
    }


    @Override
    public void setProperties(Properties properties) {
        Interceptor.super.setProperties(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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

第二步:在 MyBatis 中注册拦截器 mybatis-config.xml

<plugins>
	<plugin interceptor="com.learn.blog.demo.MyBatisPlugin">
        <--可以指定插件的参数-->
		<property />
		<property />
	</plugin>
</plugins>
1
2
3
4
5
6
7

第三步:spring 配置中指定 Mybatis 配置文件路径  image-20230629085642013

编辑 (opens new window)
上次更新: 2024/04/19, 08:52:45
MyBatis 架构与原理到执行流程
为什么 MyBatis 源码中,没有我那种 if···else

← MyBatis 架构与原理到执行流程 为什么 MyBatis 源码中,没有我那种 if···else→

最近更新
01
idea 热部署插件 JRebel 安装及破解,不生效问题解决
04-10
02
spark中代码的执行位置(Driver or Executer)
12-12
03
大数据技术之 SparkStreaming
12-12
更多文章>
Theme by Vdoing | Copyright © 2019-2024 Young | MIT License
浙ICP备20002744号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式