Mybatis 插件机制
MyBatis 拦截器(Interceptor)是 MyBatis 提供的一个强大的扩展机制,允许开发者在 SQL 执行过程中插入自定义逻辑。通过拦截器,你可以在 SQL 执行前后、参数处理、结果集处理等阶段进行干预,从而实现诸如 SQL 日志记录、分页、权限控制、性能监控等功能。
没有插件的运行图
有插件的运行图
1. 拦截器的工作原理
MyBatis 拦截器基于 Java 的动态代理机制。MyBatis 的核心组件(如 Executor、StatementHandler、ParameterHandler、ResultSetHandler 等)在执行时会被代理,拦截器可以在这些组件的方法执行前后插入自定义逻辑。
2. 拦截器的核心接口
MyBatis 拦截器的核心接口是 org.apache.ibatis.plugin.Interceptor,它定义了以下方法:
-
intercept(Invocation invocation):这是拦截器的核心方法,Invocation对象封装了被拦截的方法及其参数。你可以在这个方法中编写自定义逻辑,并决定是否继续执行被拦截的方法。 -
plugin(Object target):这个方法用于将拦截器应用到目标对象上。通常可以使用Plugin.wrap(target, this)来生成代理对象。 -
setProperties(Properties properties):这个方法用于配置拦截器的属性,可以通过 MyBatis 配置文件或注解来传递参数。
3. 实现一个简单的拦截器
下面是一个简单的 MyBatis 拦截器示例,用于记录 SQL 执行的时间:
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.mapping.BoundSql;
import java.sql.Connection;
import java.util.Properties;
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SqlExecutionTimeInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 只拦截查询sql
// if (!mappedStatement.getSqlCommandType().equals(SqlCommandType.SELECT)) {
// return invocation.proceed();
// }
long startTime = System.currentTimeMillis();
Object result = invocation.proceed(); // 继续执行被拦截的方法
long endTime = System.currentTimeMillis();
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
// 如果要修改sql(原始sql追加 id 大于 0)
// String newSql = sql + " and id > 0";
// Field field = boundSql.getClass().getDeclaredField("sql");
// field.setAccessible(true);
// field.set(boundSql, newSql);
System.out.println("SQL: " + sql);
System.out.println("Execution Time: " + (endTime - startTime) + "ms");
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
// 可以通过 properties 配置拦截器的参数
}
}
4. 配置拦截器
在 MyBatis 全局配置文件中,可以通过 <plugins> 标签来注册拦截器:
<plugins>
<plugin interceptor="com.example.SqlExecutionTimeInterceptor">
<!-- 可以在这里配置拦截器的属性 -->
<property name="someProperty" value="someValue"/>
</plugin>
</plugins>
springboot 通过配置组件来完成配置
@Configuration
public class MybatisPlugin {
@Resource
private SqlSessionFactory sqlSessionFactory;
@PostConstruct
public void addInterceptor() throws NoSuchFieldException, IllegalAccessException {
sqlSessionFactory.getConfiguration().addInterceptor(new SqlExecutionTimeInterceptor());
}
}
5. 拦截器的应用场景
- SQL 日志记录:记录执行的 SQL 语句及其执行时间,便于调试和性能分析。
- 分页:通过拦截器自动处理分页逻辑,无需在每个查询中手动编写分页代码。
- 权限控制:在 SQL 执行前检查用户权限,防止未授权的数据访问。
- 性能监控:监控 SQL 执行时间,识别慢查询。
- SQL 改写:在 SQL 执行前动态修改 SQL 语句,例如添加租户 ID 等。
6. 拦截器的执行顺序
如果有多个拦截器,MyBatis 会按照它们在配置文件中定义的顺序依次执行。每个拦截器的 plugin 方法会生成一个代理对象,后续的拦截器会继续对这个代理对象进行包装。
7. 注意事项
- 性能影响:拦截器会增加额外的开销,尤其是在高并发场景下,频繁的代理和拦截操作可能会影响性能。
- 谨慎使用:拦截器是一个强大的工具,但过度使用可能会导致代码难以维护和理解。建议只在必要时使用拦截器。
- 线程安全:拦截器本身是无状态的,但如果拦截器中使用了共享资源,需要注意线程安全问题。
8. 总结
MyBatis 拦截器提供了一种灵活的方式来扩展 MyBatis 的功能。通过拦截器,你可以在 SQL 执行的各个阶段插入自定义逻辑,从而实现各种高级功能。然而,拦截器也需要谨慎使用,避免对性能产生负面影响。

浙公网安备 33010602011771号