达永编程网

程序员技术分享与交流平台

说说Mybatis的插件原理(mybatis插件执行顺序)

解题思路

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

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

拦截器接口(Interceptor)

o 定义:插件需实现Interceptor接口,包含三个核心方法:

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

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

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

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

动态代理机制

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

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

插件链(Interceptor Chain)

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

二、插件执行流程详解

1、插件注册

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

o 示例配置:

  <plugins>
    <plugin interceptor="com.example.LoggingInterceptor">
      <property name="logLevel" value="DEBUG"/>
    </plugin>
  </plugins>

2、目标对象包装

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

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

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

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

oParameterHandler:参数处理器(参数设置)。

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

3、方法拦截与逻辑增强

o 拦截点选择:通过@Signature注解指定拦截目标:

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

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

三、插件开发示例

  1. 日志记录插件
@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、分页插件(简化版)

@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();
  }
}
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言