Mybatis的插件原理

MyBatis 的插件机制是其核心扩展能力之一,通过动态代理和拦截器模型,允许开发者在 SQL 执行的关键节点插入自定义逻辑(如日志记录、性能监控、SQL 改写等)。以下是其核心原理和实现细节的深度解析:


一、插件运行原理的核心架构

  1. 拦截器接口(Interceptor)
    • 定义:插件需实现Interceptor接口,包含三个核心方法:

    ◦intercept(Invocation invocation):拦截目标方法,执行自定义逻辑。

    ◦plugin(Object target):生成代理对象,包装目标对象。

    ◦setProperties(Properties properties):设置插件属性(通过 XML 配置传递参数)。

    • 作用:拦截器是插件的核心逻辑载体,决定何时触发拦截及如何处理。

  2. 动态代理机制
    • 代理对象生成:MyBatis 通过 Java 动态代理(Proxy.newProxyInstance)为被拦截的核心组件(如Executor、StatementHandler)生成代理类。

    • 方法拦截:当调用代理对象的方法时,会触发InvocationHandler的invoke方法,进而执行插件的intercept方法。

  3. 插件链(Interceptor Chain)
    • 多插件协作:多个插件可按配置顺序拦截同一方法,形成链式调用。每个插件的intercept方法依次执行,最终调用invocation.proceed()传递到下一个插件或原始方法。

二、插件执行流程详解

  1. 插件注册
    • 配置方式:在mybatis-config.xml中通过<plugins>标签注册插件,或使用@Intercepts注解声明拦截目标。

    • 示例配置:

    1

    2

    3

    4

    5

    <plugins>

      <plugin interceptor="com.example.LoggingInterceptor">

        <property name="logLevel" value="DEBUG"/>

      </plugin>

    </plugins>

  2. 目标对象包装
    • 代理生成时机:MyBatis 初始化时扫描所有插件,根据@Signature注解匹配目标类和方法,生成代理对象。

    • 核心组件拦截:插件可作用于以下四个核心接口:

    ◦Executor:SQL 执行器(如update、query方法)。

    ◦StatementHandler:SQL 语句处理器(如预编译、参数绑定)。

    ◦ParameterHandler:参数处理器(参数设置)。

    ◦ResultSetHandler:结果集处理器(结果映射)。

  3. 方法拦截与逻辑增强
    • 拦截点选择:通过@Signature注解指定拦截目标:

    1

    2

    3

    @Intercepts({

      @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})

    })

    • 逻辑插入:在intercept方法中,可通过invocation.getArgs()获取方法参数,通过invocation.proceed()调用原始方法,实现日志记录、SQL 改写等操作。


三、插件开发示例

  1. 日志记录插件

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    @Intercepts({

      @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})

    })

    public class SqlLogInterceptor implements Interceptor {

      @Override

      public Object intercept(Invocation invocation) throws Throwable {

        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];

        Object parameter = invocation.getArgs()[1];

        String sql = ms.getBoundSql(parameter).getSql();

        System.out.println("Executing SQL: " + sql);

        long start = System.currentTimeMillis();

        Object result = invocation.proceed();

        System.out.println("Execution time: " + (System.currentTimeMillis() - start) + "ms");

        return result;

      }

      @Override

      public Object plugin(Object target) {

        return Plugin.wrap(target, this);

      }

    }

  2. 分页插件(简化版)

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    @Intercepts({

      @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})

    })

    public class PaginationInterceptor implements Interceptor {

      @Override

      public Object intercept(Invocation invocation) throws Throwable {

        StatementHandler handler = (StatementHandler) invocation.getTarget();

        MappedStatement ms = handler.getMappedStatement();

        Object parameter = handler.getParameterHandler().getParameterObject();

        // 动态改写 SQL 添加分页逻辑(如 LIMIT/OFFSET)

        BoundSql boundSql = handler.getBoundSql();

        String originalSql = boundSql.getSql();

        String pageSql = originalSql + " LIMIT 0, 10"// 示例分页

        // 通过反射修改 BoundSql 中的 SQL

        Field field = boundSql.getClass().getDeclaredField("sql");

        field.setAccessible(true);

        field.set(boundSql, pageSql);

        return invocation.proceed();

      }

    }


四、插件应用场景

场景实现方式典型插件
SQL 日志记录拦截Executor.update和query方法p6spy、MyBatis-Log4j
分页查询拦截StatementHandler.prepare方法PageHelper
性能监控统计 SQL 执行时间MyBatis-Performance
数据加密/解密拦截ParameterHandler.setParametersJasypt-MyBatis
自动事务管理拦截Executor.commit/rollback方法自定义事务插件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值