JAVA:如何编写一个 MyBatis 插件的技术指南

admin
6
2025-09-01

1、简述

MyBatis 除了强大的 ORM 功能,还提供了 插件(Interceptor)机制,允许我们在执行 SQL 的生命周期中进行拦截和增强。
常见的应用场景有:

🔹 SQL 日志打印和性能监控

🔹 数据权限控制

🔹 分页拦截

🔹 参数加解密

本文将详细介绍 MyBatis 插件的原理、编写方式,并通过实践样例展示如何实现 SQL 执行耗时监控插件

image-bsxq.png


2、MyBatis 插件的原理

MyBatis 通过 四大对象的动态代理来实现插件拦截:

🔹 Executor —— SQL 执行器

🔹 StatementHandler —— 处理 SQL 语句

🔹 ParameterHandler —— 处理参数

🔹 ResultSetHandler —— 处理结果集

我们可以通过 @Intercepts@Signature 注解指定要拦截的对象和方法。


3、编写 MyBatis 插件的步骤

🔹 实现 org.apache.ibatis.plugin.Interceptor 接口

🔹 在 @Intercepts 注解中声明拦截点

🔹 实现 intercept 方法,编写增强逻辑

🔹 在 MyBatis 配置文件或 Spring Boot 配置类中注册插件


4、实践样例

4.1 自定义插件类

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Properties;

@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SqlCostInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long start = System.currentTimeMillis();
    
        // 执行原方法
        Object result = invocation.proceed();
    
        long end = System.currentTimeMillis();
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        Statement statement = (Statement) invocation.getArgs()[0];
    
        System.out.println("[MyBatis SQL Monitor] SQL: " 
                + statementHandler.getBoundSql().getSql()
                + " | Cost: " + (end - start) + " ms");
    
        return result;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 可通过配置传入参数
    }
}

4.2 在 MyBatis 中注册插件

方式一:mybatis-config.xml

<plugins>
    <plugin interceptor="com.example.mybatis.SqlCostInterceptor"/>
</plugins>

方式二:Spring Boot 配置类

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisConfig {

    @Bean
    public SqlSessionFactory sqlSessionFactory(javax.sql.DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);

        // 注册插件
        SqlCostInterceptor interceptor = new SqlCostInterceptor();
        factoryBean.setPlugins(interceptor);

        return factoryBean.getObject();
    }
}

4.3 测试代码

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Autowired
    private UserMapper userMapper;

    @GetMapping("/test")
    public Object test() {
        return userMapper.selectUserById(1);
    }
}

4.4 输出结果

运行后控制台日志:

[MyBatis SQL Monitor] SQL: SELECT id, username FROM users WHERE id = ? | Cost: 15 ms

5、总结

🔹 拦截原理:MyBatis 使用动态代理,在四大对象的执行方法上插入自定义逻辑。

🔹 插件结构:实现 Interceptor → 定义 @Intercepts → 注册插件。

🔹 适用场景:日志监控、权限校验、分页插件、SQL 审计、数据加解密等。

🔹 注意事项: 插件过多可能影响性能,避免在插件里写复杂业务逻辑,合理选择拦截点(Executor / StatementHandler 等)

动物装饰