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 结构与内存优化
相关推荐
梁同学与Android1 小时前
Android ---【内存优化】常见的内存泄露以及解决方案
android·java·内存泄漏
云和数据.ChenGuang1 小时前
gitlab-ci.yml
面试·职场和发展·gitee·运维面试题·运维技术总结
武子康2 小时前
Java-71 深入浅出 RPC Dubbo 上手 父工程配置编写 附详细POM与代码
java·分布式·程序人生·spring·微服务·rpc·dubbo
武子康4 小时前
Java-72 深入浅出 RPC Dubbo 上手 生产者模块详解
java·spring boot·分布式·后端·rpc·dubbo·nio
_殊途5 小时前
《Java HashMap底层原理全解析(源码+性能+面试)》
java·数据结构·算法
椰椰椰耶6 小时前
【Spring】拦截器详解
java·后端·spring
没有羊的王K8 小时前
SSM框架学习——day1
java·学习
又菜又爱coding8 小时前
安装Keycloak并启动服务(macOS)
java·keycloak
不知道叫什么呀9 小时前
【C】vector和array的区别
java·c语言·开发语言·aigc