Mybatis拦截器原理解析
一、概述
在Mybatis开发体系中,“过滤器”是开发者对Mybatis拦截器(Plugin)的通俗称呼,它是Mybatis提供的核心扩展机制——无需修改框架源码,就能对SQL执行全流程进行增强,比如分页插件、通用字段自动填充、数据加解密等功能,底层均依赖这一机制。本文将从底层技术原理、核心源码解析到实战开发,完整拆解Mybatis过滤器的实现逻辑,还原其设计精髓。
二、核心对象
2.1 可拦截的核心接口
MyBatis仅允许拦截其内部4个核心接口的方法,这4个接口覆盖了SQL执行的全流程,开发者需通过@Intercepts注解指定拦截目标:
| 拦截接口 | 核心作用 | 常用拦截方法 | 典型应用场景 |
|---|---|---|---|
| Executor | SQL执行器(增删改查入口) | query/update/commit | 耗时统计、分页插件、数据权限控制 |
| ParameterHandler | 参数处理器(设置SQL预编译参数) | setParameters | 参数加密、参数校验、默认值填充 |
| ResultSetHandler | 结果集处理器(封装查询结果) | handleResultSets | 结果集解密、数据脱敏、格式转换 |
| StatementHandler | SQL语句处理器(创建Statement对象) | prepare/parameterize | SQL改写、SQL日志打印、防注入处理 |

2.2 拦截器核心接口
自定义拦截器必须实现org.apache.ibatis.plugin.Interceptor接口,该接口包含3个核心方法:
| 方法名 | 作用说明 |
|---|---|
| intercept(Invocation invocation) | 核心拦截逻辑入口,SQL执行时触发,可修改参数、增强结果、统计信息等 |
| plugin(Object target) | 为目标对象创建动态代理,推荐使用MyBatis提供的Plugin.wrap()方法实现 |
| setProperties(Properties properties) | 读取拦截器配置属性(如从配置文件传入的密钥、日志级别等),可选实现 |
2.3 关键注解
- @Intercepts:标识该类为
MyBatis拦截器,内部可包含多个@Signature注解,指定拦截的接口、方法和参数。 - @Signature:定义具体的拦截规则,包含3个属性:
- type:拦截的核心接口(如
Executor.class); - method:拦截的方法名(如
query); - args:拦截方法的参数类型数组(必须与接口方法参数完全匹配)。
- type:拦截的核心接口(如
三、工作流程
MyBatis拦截器的执行遵循“动态代理+责任链”模式,完整流程如下:
- 初始化阶段:
Spring Boot启动时,通过配置类将自定义拦截器注册到SqlSessionFactory; - 代理创建阶段:
MyBatis初始化核心接口(如Executor)时,调用拦截器的plugin()方法,为目标接口创建动态代理; - 方法拦截阶段:执行
Mapper方法触发SQL时,代理对象拦截目标方法调用,进入intercept()方法; - 自定义逻辑执行:在
intercept()中执行前置增强(如记录开始时间、修改参数); - 原方法执行:通过
invocation.proceed()调用被拦截的原方法,执行SQL操作; - 后置处理阶段:原方法执行完成后,执行后置增强(如统计耗时、解密结果集);
- 结果返回:将处理后的结果返回给调用者。
若注册了多个拦截器,将按“注册顺序逆序执行”(责任链模式),例如注册顺序为拦截器A→拦截器B→拦截器C,实际执行顺序为拦截器C→拦截器B→拦截器A。
四、底层实现
Mybatis过滤器的实现依赖两大基础技术,我们先通过极简示例还原核心逻辑,再对接Mybatis源码。
4.1 基础:JDK动态代理核心实现
JDK动态代理是过滤器的“增强载体”,通过InvocationHandler对目标对象方法进行前置/后置增强,先看基础源码实现:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 目标接口(模拟Mybatis核心接口)
public interface Executor {
void query(String sql);
}
// 目标实现类(模拟Mybatis默认Executor)
public class SimpleExecutor implements Executor {
@Override
public void query(String sql) {
System.out.println("执行SQL:" + sql);
}
}
// 动态代理处理器(核心增强逻辑)
public class PluginInvocationHandler implements InvocationHandler {
// 目标对象(如Executor实例)
private final Object target;
// 拦截器(过滤器)实例
private final Interceptor interceptor;
public PluginInvocationHandler(Object target, Interceptor interceptor) {
this.target = target;
this.interceptor = interceptor;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 执行过滤器的拦截逻辑
Object result = interceptor.intercept(new Invocation(target, method, args));
return result;
}
// 生成代理对象的工具方法
public static Object wrap(Object target, Interceptor interceptor) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new PluginInvocationHandler(target, interceptor)
);
}
}
// 抽象过滤器(拦截器)接口
public interface Interceptor {
// 核心拦截方法
Object intercept(Invocation invocation) throws Throwable;
// 为目标对象创建代理(绑定过滤器)
default Object plugin(Object target) {
return PluginInvocationHandler.wrap(target, this);
}
}
// 方法调用封装类(封装目标对象、方法、参数)
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 proceed() throws Throwable {
return method.invoke(target, args);
}
// getter方法
public Object getTarget() { return target; }
public Method getMethod() { return method; }
public Object[] getArgs() { return args; }
}
4.2 进阶:责任链模式管理多过滤器
当存在多个过滤器时,需要通过“拦截器链”管理执行顺序,这是责任链模式的典型应用,源码如下:
import java.util.ArrayList;
import java.util.List;
// 过滤器链(拦截器链)
public class InterceptorChain {
// 存储所有注册的过滤器
private final List<Interceptor> interceptors = new ArrayList<>();
// 为目标对象绑定所有过滤器(链式代理)
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
// 添加过滤器
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
// 获取所有过滤器
public List<Interceptor> getInterceptors() {
return new ArrayList<>(interceptors);
}
}
// 测试:多过滤器链式执行
public class TestProxy {
public static void main(String[] args) throws Throwable {
// 1. 创建目标对象
Executor target = new SimpleExecutor();
// 2. 创建过滤器链并注册过滤器
InterceptorChain chain = new InterceptorChain();
// 过滤器1:日志增强
chain.addInterceptor(new Interceptor() {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("【日志过滤器】前置增强:执行SQL前打印日志");
Object result = invocation.proceed();
System.out.println("【日志过滤器】后置增强:执行SQL后打印日志");
return result;
}
});
// 过滤器2:性能监控
chain.addInterceptor(new Interceptor() {
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long end = System.currentTimeMillis();
System.out.println("【性能过滤器】SQL执行耗时:" + (end - start) + "ms");
return result;
}
});
// 3. 绑定所有过滤器,生成代理对象
Executor proxyExecutor = (Executor) chain.pluginAll(target);
// 4. 执行目标方法
proxyExecutor.query("SELECT * FROM user WHERE id = 1");
}
}
执行结果:
【日志过滤器】前置增强:执行SQL前打印日志
【性能过滤器】前置增强:开始计时
执行SQL:SELECT * FROM user WHERE id = 1
【性能过滤器】后置增强:SQL执行耗时:0ms
【日志过滤器】后置增强:执行SQL后打印日志
上述示例完整还原了Mybatis过滤器的核心逻辑:通过JDK动态代理实现单方法增强,通过责任链模式实现多过滤器的链式执行。
五、核心源码解析
Mybatis源码中对过滤器的实现与上述示例高度一致,以下是核心类的源码拆解:
5.1 核心接口:Interceptor
Mybatis定义的过滤器核心接口,所有自定义过滤器必须实现此接口:
package org.apache.ibatis.plugin;
import java.util.Properties;
public interface Interceptor {
// 核心拦截方法:编写自定义增强逻辑
Object intercept(Invocation invocation) throws Throwable;
// 为目标对象创建代理对象
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
// 设置过滤器配置属性(从mybatis-config.xml读取)
default void setProperties(Properties properties) {
// 空实现,可自定义覆盖
}
}
5.2 代理核心类:Plugin
Mybatis内置的InvocationHandler实现类,负责动态代理的创建和方法拦截判断:
package org.apache.ibatis.plugin;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Plugin implements InvocationHandler {
// 目标对象(如Executor、StatementHandler实例)
private final Object target;
// 当前过滤器实例
private final Interceptor interceptor;
// 过滤器注解配置的拦截方法映射(@Intercepts + @Signature)
private final Map<Class<?>, Set<Method>> signatureMap;
private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
// 生成代理对象的核心方法
public static Object wrap(Object target, Interceptor interceptor) {
// 解析过滤器的@Intercepts注解,获取需要拦截的方法
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
// 获取目标对象的所有接口(仅拦截Mybatis四大核心接口)
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
// 存在可拦截接口则生成代理,否则返回原对象
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap)
);
}
return target;
}
// 代理方法执行入口:判断是否拦截当前方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 获取当前方法所属接口的拦截方法集合
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
// 若当前方法需要拦截,则执行过滤器逻辑
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
// 无需拦截则直接执行原方法
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
// 解析@Intercepts注解,生成拦截方法映射
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
// 无@Intercepts注解则抛出异常
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor "
+ interceptor.getClass().getName());
}
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type()
+ " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
// 获取目标对象的所有可拦截接口
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
Set<Class<?>> interfaces = new HashSet<>();
while (type != null) {
for (Class<?> c : type.getInterfaces()) {
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class<?>[0]);
}
}
5.3 过滤器链:InterceptorChain
Mybatis用于管理所有注册过滤器的核心类,负责为目标对象绑定所有过滤器:
package org.apache.ibatis.plugin;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();
// 为目标对象依次绑定所有过滤器(链式代理)
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
// 添加过滤器
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
// 获取不可变的过滤器列表
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
5.4 初始化入口:Configuration
Mybatis在创建四大核心接口实例时,会通过InterceptorChain绑定所有过滤器,以Executor为例:
package org.apache.ibatis.session;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.SimpleExecutor;
import org.apache.ibatis.executor.ReuseExecutor;
import org.apache.ibatis.executor.BatchExecutor;
import org.apache.ibatis.plugin.InterceptorChain;
public class Configuration {
// 过滤器链实例
protected final InterceptorChain interceptorChain = new InterceptorChain();
// 创建Executor实例并绑定过滤器
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
// 创建不同类型的Executor实例
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
// 开启缓存则包装为CachingExecutor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 为Executor绑定所有过滤器(核心步骤)
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
// ParameterHandler/StatementHandler/ResultSetHandler创建逻辑同理
// ...
}
六、使用注意事项
- 拦截范围限制:仅能拦截
Executor、ParameterHandler、StatementHandler、ResultSetHandler四大接口的方法,非核心接口无法拦截; - 注解配置规范:
@Signature中的method必须与目标接口方法名一致,args必须与方法参数类型完全匹配(顺序、类型均不能错); - 性能影响:每一次拦截都会触发动态代理的
invoke方法,过多过滤器会增加方法调用层级,需按需开发; - 线程安全:过滤器实例为单例,若过滤器中包含成员变量,需保证线程安全;
- 避免循环拦截:在拦截方法中调用目标对象方法时,避免再次触发自身拦截逻辑。
七、总结
Mybatis过滤器(拦截器)是JDK动态代理与责任链模式的经典应用,其核心设计思路可总结为:
- 精准拦截:通过
@Intercepts+@Signature注解精准定位需要增强的核心接口和方法; - 链式增强:通过
InterceptorChain管理多个过滤器,按注册顺序依次执行增强逻辑; - 低侵入扩展:无需修改
Mybatis源码,通过实现Interceptor接口即可扩展核心功能。
掌握过滤器原理后,不仅能灵活实现分页、字段填充、数据加解密等常见需求,还能基于此定制化扩展Mybatis的核心能力,是Mybatis进阶开发的必备知识点。

浙公网安备 33010602011771号