MyBatis拦截器通过MappedStatement.getId()解析出Mapper接口类和方法名,再反射获取对应Method对象,从而读取其上的自定义注解(需@Retention(RUNTIME)且@Target(METHOD));XML中无法直接加Java注解,应改用注解式Mapper或扩展XML属性方案。

MyBatis 的 Interceptor 可以在执行 SQL 前后拦截 MappedStatement,而要读取其上的自定义注解,核心在于:MappedStatement 本身不直接持有注解,注解实际是写在对应的 @Select、@Update 等接口方法或 XML 中的 SQL 标签上;MyBatis 在构建 MappedStatement 时会把接口方法(Method)信息存入其 sqlSource 或 configuration 相关结构中,但最可靠、通用的方式是通过 MappedStatement 获取绑定的 Method 对象,再用反射读取该方法上的注解。
确认注解定义和使用位置
你的自定义注解必须声明为 @Retention(RetentionPolicy.RUNTIME),且目标为 @Target(ElementType.METHOD)(如果标注在 Mapper 接口方法上):
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyCustomAnnotation {
String value() default "";
}
然后在 Mapper 接口中使用:
public interface UserMapper {
@MyCustomAnnotation("log-this")
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(@Param("id") Long id);
}
在 Interceptor 中获取 Method 并读取注解
MappedStatement 本身没有直接暴露 Method,但可通过以下路径还原:
- 从
MappedStatement获取configuration→ 找到对应MapperRegistry→ 定位到该 statement 的mapperType(即 Mapper 接口 Class) - 再结合
MappedStatement.getId()(格式通常是com.example.UserMapper.selectById),提取方法名selectById - 用反射从
mapperType中查找同名Method,注意参数类型需匹配(可借助ParamNameResolver或简化处理:若只用于注解读取,可忽略重载,用名称+参数个数粗筛)
更轻量、推荐的做法(适用于注解在接口方法上的场景):
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
// 在 Interceptor 的 intercept 方法中
Object target = invocation.getTarget();
if (target instanceof Executor) {
// 拦截的是 Executor#query/update 等,参数[0] 是 MappedStatement
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
<pre class="brush:php;toolbar:false;">// Step 1: 解析 id 获取 mapper 接口类和方法名
String id = ms.getId(); // e.g. "com.example.UserMapper.selectById"
int lastDot = id.lastIndexOf('.');
if (lastDot > 0) {
String className = id.substring(0, lastDot);
String methodName = id.substring(lastDot + 1);
try {
Class> mapperClass = Resources.classForName(className);
// 尝试获取无参或单参方法(常见情况)
Method method = Arrays.stream(mapperClass.getDeclaredMethods())
.filter(m -> m.getName().equals(methodName))
.findFirst()
.orElse(null);
if (method != null && method.isAnnotationPresent(MyCustomAnnotation.class)) {
MyCustomAnnotation ann = method.getAnnotation(MyCustomAnnotation.class);
System.out.println("Found annotation: " + ann.value());
// ✅ 此处可做日志、权限校验、SQL 改写等
}
} catch (ClassNotFoundException e) {
// 忽略或记录 warn
}
}
}
XML 映射文件中的注解怎么办?
XML 方式(如 )无法直接加 Java 注解。此时有两种替代方案:
- 改用 注解式 Mapper:把 SQL 移到接口方法上,才能自然使用自定义注解
- 在 XML 的
标签里加自定义属性,例如,并在 Interceptor 中解析ms.getSqlSource()是否为XMLLanguageDriver创建的XMLScriptBuilder,再从ms.getBoundSql(...).getAdditionalParameters()或扩展XMLConfigBuilder提前注入元数据 —— 但复杂度高,不推荐
注意事项与避坑点
反射读取注解看似简单,但容易踩坑:
-
方法可能被代理:Spring AOP 或 MyBatis 自身代理可能导致
ms.getId()和实际接口类不一致,建议优先用ms.getConfiguration().getMapperRegistry().getMappers()查找已注册的 mapper 类 -
泛型/重载方法识别不准:仅靠方法名匹配可能出错,生产环境建议缓存
Map(key 为ms.getId()),在应用启动时预扫描所有 Mapper 接口 -
性能考虑:避免每次拦截都重复
Class.forName和getDeclaredMethods(),应提前构建好Map缓存
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










