JAVA面试宝典 - 《MyBatis 进阶:插件开发与二级缓存》

文章目录

  • [🚀《MyBatis 进阶:插件开发与二级缓存》](#🚀《MyBatis 进阶:插件开发与二级缓存》)
    • [一、开场引导:MyBatis 不只是 ORM 工具](#一、开场引导:MyBatis 不只是 ORM 工具)
    • [二、MyBatis 插件机制详解](#二、MyBatis 插件机制详解)
      • [2.1 插件本质是"执行栈拦截器"](#2.1 插件本质是“执行栈拦截器”)
      • [2.2 插件开发实战:记录 SQL 执行耗时](#2.2 插件开发实战:记录 SQL 执行耗时)
      • [2.3 插件原理揭秘](#2.3 插件原理揭秘)
    • [三、MyBatis 二级缓存机制](#三、MyBatis 二级缓存机制)
      • [3.1 一级缓存 vs 二级缓存](#3.1 一级缓存 vs 二级缓存)
      • [3.2 启用二级缓存的步骤](#3.2 启用二级缓存的步骤)
    • [3.3 缓存失效与扩展](#3.3 缓存失效与扩展)
    • [四、实战案例:自定义 Cache 接入 Redis](#四、实战案例:自定义 Cache 接入 Redis)
      • [4.1 实现 Cache 接口](#4.1 实现 Cache 接口)
      • [4.2 注册自定义缓存](#4.2 注册自定义缓存)
    • 五、常见问题与性能建议
    • 六、总结
    • [📚 推荐阅读](#📚 推荐阅读)

🚀《MyBatis 进阶:插件开发与二级缓存》

🎯 目标读者

已掌握 MyBatis 基础映射与 Mapper 使用,

想深入了解 MyBatis 插件机制与二级缓存原理及实践。


一、开场引导:MyBatis 不只是 ORM 工具

MyBatis 以 灵活可插拔轻量级 著称,但你是否真正了解它底层"魔法"?

  • 插件 能干预 SQL 执行流程:日志、权限、监控......
  • 二级缓存SqlSession 提升查询性能:内存、分布式都可扩展!

问题抛出:

  • 如何开发一个统计 SQL 执行耗时的插件?
  • 二级缓存如何从内存跑到 Redis?

二、MyBatis 插件机制详解

2.1 插件本质是"执行栈拦截器"

类比:Spring AOP 是"分层切面",MyBatis 插件更像给执行栈每一站"排队拦车"------

每到关键节点,拦下并加入自定义逻辑,再放行。

可拦截的四大方法(对应核心四大处理器):

处理器 可拦截方法
Executor query()update()
ParameterHandler setParameters()
ResultSetHandler handleResultSets()
StatementHandler prepare()parameterize()batch()

2.2 插件开发实战:记录 SQL 执行耗时

步骤一:实现 Interceptor
java 复制代码
@Intercepts({
  @Signature(type = Executor.class,
             method = "query",
             args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class SqlTimingInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long start = System.nanoTime();
        Object result = invocation.proceed();
        long elapsedMs = (System.nanoTime() - start) / 1_000_000;
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        if (elapsedMs > 100) {
            System.err.printf("[Slow SQL] %s took %d ms%n", ms.getId(), elapsedMs);
        }
        return result;
    }
    @Override public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
    @Override public void setProperties(Properties props) { }
}
步骤二:注册插件
xml 复制代码
<plugins>
  <plugin interceptor="com.example.mybatis.SqlTimingInterceptor"/>
</plugins>

2.3 插件原理揭秘

  1. Plugin.wrap(target, interceptor)
  • 如果目标类型匹配 @Signature,则用 JDK 动态代理或 CGLIB 生成代理对象。
  1. 链式拦截
  • 多个插件按配置顺序"嵌套",先注册的最外层,最后执行的最内层。
  1. 执行流程
java 复制代码
ExecutorImpl.query() 
  → interceptor1.intercept() 
    → interceptor2.intercept() 
      → 实际 query 方法

三、MyBatis 二级缓存机制

3.1 一级缓存 vs 二级缓存

缓存类型 范围 生命周期 默认开启 可配置性
一级缓存 单个 SqlSession 会话内 不可关闭
二级缓存 Mapper 映射空间 SqlSession 可开启/禁用
  • 一级缓存:类似图书馆阅览室,只在"当天"可重复借阅。

  • 二级缓存:像书架借书,可以跨会话"长期"缓存(甚至跨进程)。

3.2 启用二级缓存的步骤

  1. Mapper 接口/映射文件 加上注解或 XML:
java 复制代码
@CacheNamespace(implementation=RedisCache.class, eviction=FifoCache.class, flushInterval=60000)
public interface UserMapper { ... }
  1. 实体类实现 Serializable
  2. 查询方法返回可序列化对象,不能是流或 ResultHandler

写操作(insert/update/delete)会清空对应 namespace 下的二级缓存。

3.3 缓存失效与扩展

  • Session 关闭:一级缓存清空
  • 写操作:清空二级缓存
  • 命名空间隔离:不同 Mapper 之间不共享
  • 自定义缓存:可实现 org.apache.ibatis.cache.Cache 接口,用 Redis、Guava 等

四、实战案例:自定义 Cache 接入 Redis

4.1 实现 Cache 接口

java 复制代码
public class RedisCache implements Cache {
    private final String id;
    private RedisTemplate<String,Object> redis;
    public RedisCache(String id) { this.id = id; }
    @Override public String getId() { return id; }
    @Override public void putObject(Object key, Object value) {
        redis.opsForHash().put(id, key.toString(), value);
    }
    @Override public Object getObject(Object key) {
        return redis.opsForHash().get(id, key.toString());
    }
    @Override public Object removeObject(Object key) {
        return redis.opsForHash().delete(id, key.toString());
    }
    @Override public void clear() {
        redis.delete(id);
    }
    @Override public int getSize() {
        return redis.opsForHash().size(id).intValue();
    }
}

4.2 注册自定义缓存

xml 复制代码
<cache type="com.example.mybatis.RedisCache"/>

发布到分布式集群时,所有节点都可共享同一 Redis 二级缓存。

五、常见问题与性能建议

问题 建议
插件过多影响性能 精简插件链,按需拦截;生产环境剔除非必要插件
二级缓存命中率低 减少写操作频率;针对热点数据单独设计缓存清理策略
数据一致性难保证 配合消息队列或 Redis 发布/订阅实现失效通知
统计所有 SQL Executor.query()update() 中同时拦截

六、总结

  • 插件机制:通过 Interceptor 拦截执行栈,灵活扩展 SQL 监控、审计、权限等功能。
  • 二级缓存:跨会话缓存结果,默认内存实现,可自定义 Redis、Guava 等持久化方式。
  • 进阶能力:掌握插件与缓存设计,意味着你已步入 MyBatis 的"进阶圈",能让 ORM 不止"映射",还能"加速"与"智能化"!

📚 推荐阅读

  • MyBatis 官方文档:插件开发 & 缓存章节
  • MyBatis 源码深度解析
  • Redis 官方文档:Hash 结构与内存优化
相关推荐
我是一颗柠檬3 小时前
【MySQL全面教学】MySQL面试高频考点汇总Day15(2026年)
数据库·后端·mysql·面试
橙淮4 小时前
并发编程(六)
java·jvm
拽着尾巴的鱼儿4 小时前
springboot openfeign 自定义feign 接口重试机制
java·spring boot·后端
白露与泡影4 小时前
2026大厂Java面试题大全!牛客网最新版
java·开发语言
EntyIU5 小时前
JVM内存与GC笔记
java·jvm·笔记
swipe5 小时前
DeepAgents 实战:用多 Agent 架构搭一个深度调研助手
javascript·面试·llm
XS0301065 小时前
并发编程 六
java·后端
yaoxin5211235 小时前
419. 现代 Java IO 最佳实践 - 写入文本文件
java·windows·python
雪宫街道5 小时前
synchronized 锁的范围:对象锁、类锁与代码块锁
java·jvm·后端·面试
x***r1515 小时前
linux安装 jdk-8u291-linux-x64.tar.gz 详细步骤(解压配置环境变量)
java