Java面试——MyBatis的插件原理与一级二级缓存

畫卷琴夢

畫卷琴夢

2026-04-14

762人浏览

原创

mybatis插件通过jdk动态代理拦截executor、statementhandler等四个接口的public非final方法,需实现interceptor接口并用@intercepts精确声明@signature;一级缓存绑定sqlsession生命周期;二级缓存需满足全局启用、mapper声明、pojo可序列化、sql未禁用缓存四条件;插件执行在缓存外层,顺序为plugin→cachingexecutor→baseexecutor。

java面试——mybatis的插件原理与一级二级缓存

MyBatis插件怎么拦截Executor、StatementHandler这些对象

MyBatis插件本质是基于JDK动态代理,只对ExecutorParameterHandlerResultSetHandlerStatementHandler这四个接口生效。不是所有方法都能被拦截,必须是接口中定义的、且未被final修饰的public方法。

关键点在于:插件类需实现Interceptor接口,并在@Intercepts注解里明确指定@Signature——包括type(目标接口)、method(方法名)、args(参数类型数组)。漏写args或类型不匹配会导致代理失效,根本不会进入intercept()方法。

常见错误现象:
- 插件类加了@Intercepts但没生效 → 检查args是否与目标方法签名完全一致(比如query(Statement, ResultHandler)不能写成query(Object, Object)
- 拦截Executor.update()时发现入参是MappedStatement而非String → 因为实际调用的是Executor.doUpdate(MappedStatement, Object),真正执行SQL的是底层StatementHandler

@Intercepts({
  @Signature(type = Executor.class, method = "update", 
             args = {MappedStatement.class, Object.class})
})
public class MyPlugin implements Interceptor {
  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    System.out.println("before update");
    Object result = invocation.proceed(); // 必须调用,否则链中断
    System.out.println("after update");
    return result;
  }
}

一级缓存为什么在同一个SqlSession里才生效

一级缓存是BaseExecutor内部维护的PerpetualCache,生命周期与SqlSession绑定。只要SqlSession没关闭或没调用clearCache(),后续相同MappedStatement + 相同参数的查询就会命中缓存。

Java JDK 25
Java JDK 25

Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。

下载

容易踩的坑:
- 多线程共用一个SqlSession → 缓存状态混乱,可能读到脏数据
- 执行了insert/update/delete操作后,缓存自动清空 → 这是MyBatis默认行为,由BaseExecutor.clearLocalCacheOnUpdate()触发,无需手动干预
- 使用RowBounds分页时缓存失效 → 因为RowBounds参与key计算,两次offset/limit不同则key不同

验证方式:开启log4j.logger.org.apache.ibatis.cache=DEBUG,看到Cache Hit Ratio统计即可确认是否命中。

二级缓存开启后为什么还是没生效

二级缓存是Mapper级别(即namespace),默认关闭,必须同时满足四个条件:
- 全局配置cacheEnabled=true(默认true,但显式设为false会禁用)
- Mapper XML中声明<cache></cache>或使用@CacheNamespace注解
- 对应的POJO实现Serializable接口(否则反序列化失败,缓存写入直接跳过)
- 执行的SQL语句所在的MappedStatement没有设置useCache="false"

典型问题:
- 开启了二级缓存但日志里始终没有Cache Hit Ratio → 检查POJO是否真的可序列化(包括其所有字段类型,如含ThreadLocalInputStream会报NotSerializableException
- 同一Mapper下部分查询走缓存、部分不走 → 查看对应<select></select>标签是否写了useCache="false",或是否用了flushCache="true"
- 多个SqlSession查同一数据,仍发SQL → 确认是否提交了事务(二级缓存仅在commit后写入,未提交前其他session不可见)

缓存和插件谁先执行、会不会互相干扰

执行顺序是固定的:插件链在最外层,缓存逻辑在CachingExecutor内部。当启用二级缓存时,MyBatis会把原始Executor包装成CachingExecutor,而你的插件又会去代理这个CachingExecutor。所以调用链是:Plugin → CachingExecutor → BaseExecutor

这意味着:
- 如果你在插件里修改了Invocation.args(比如篡改SQL参数),会影响后续缓存key的计算 → 可能导致缓存击穿或误命中
- CachingExecutorquery()方法本身也会被插件拦截,此时invocation.getTarget()返回的是CachingExecutor实例,不是BaseExecutor
- 不建议在插件中直接操作Executor.localCache,因为二级缓存场景下它只是临时存储,最终以delegate(即CachingExecutor持有的Cache)为准

最易忽略的一点:插件的plugin()方法返回代理对象,如果忘了调用Plugin.wrap(target, this),整个插件就形同虚设——Spring Boot里常因没在@Configuration类中正确注册Interceptor bean而导致此问题。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

java

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

2427

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

447

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

613

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

3943

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

1303

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

3540

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

3440

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

639

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

525

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习

Java 26官方文档
Java 26官方文档

共0课时 | 0人学习