手写MyBatis第78弹:装饰器模式在MyBatis二级缓存中的应用:从LRU到防击穿的全方案实现

  1. MyBatis二级缓存装饰器模式深度解析:从LRU到防击穿的全方案实现

  2. 手写MyBatis缓存装饰器:SynchronizedCache与BlockingCache实战

  3. 装饰器模式在MyBatis缓存中的应用:灵活组合的架构设计

  4. 二级缓存高级特性:序列化、LRU淘汰、日志统计完整实现

  5. MyBatis缓存装饰器链:如何设计可扩展的CacheBuilder

目录

正文

一、装饰器模式在缓存架构中的核心价值

[装饰器模式 vs 继承的优劣对比](#装饰器模式 vs 继承的优劣对比)

二、核心缓存装饰器实现详解

[1. SynchronizedCache:线程安全的基础保障](#1. SynchronizedCache:线程安全的基础保障)

[2. LoggingCache:缓存命中率监控](#2. LoggingCache:缓存命中率监控)

[3. SerializedCache:对象序列化与副本保护](#3. SerializedCache:对象序列化与副本保护)

[4. LruCache:内存资源智能管理](#4. LruCache:内存资源智能管理)

[5. BlockingCache:防止缓存击穿的保护盾](#5. BlockingCache:防止缓存击穿的保护盾)

三、CacheBuilder:装饰器的灵活组合器

四、装饰器执行顺序的架构考量

正确的顺序设计

顺序设计原则

五、生产环境中的装饰器实践

[1. 性能敏感场景的优化](#1. 性能敏感场景的优化)

[2. 分布式缓存集成](#2. 分布式缓存集成)

[3. 缓存预热策略](#3. 缓存预热策略)

六、装饰器模式的架构思想延伸

七、总结


🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞

💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论

🔥🔥🔥(源码 + 调试运行 + 问题答疑)

🔥🔥🔥 有兴趣可以联系我。文末有免费源码

免费获取源码。

更多内容敬请期待。如有需要可以联系作者免费送

更多源码定制,项目修改,项目二开可以联系作者

点击可以进行搜索(每人免费送一套代码):千套源码目录(点我)

2025元旦源码免费送(点我)

我们常常在当下感到时间慢,觉得未来遥远,但一旦回头看,时间已经悄然流逝。对于未来,尽管如此,也应该保持一种从容的态度,相信未来仍有许多可能性等待着我们。


正文

一、装饰器模式在缓存架构中的核心价值

装饰器模式(Decorator Pattern)是MyBatis二级缓存架构的精髓所在。它通过动态组合的方式,为缓存功能添加各种增强特性,而不需要修改原有的缓存实现。这种设计符合开闭原则,使得系统具有良好的扩展性和灵活性。

装饰器模式 vs 继承的优劣对比
  • 继承的局限性:如果使用继承,要实现所有功能组合需要创建大量子类

  • 装饰器的优势:通过组合方式,可以动态地、透明地添加功能

二、核心缓存装饰器实现详解

1. SynchronizedCache:线程安全的基础保障
java 复制代码
public class SynchronizedCache implements Cache {
     private final Cache delegate;
     
     public SynchronizedCache(Cache delegate) {
         this.delegate = delegate;
     }
     
     @Override
     public synchronized void putObject(Object key, Object value) {
         delegate.putObject(key, value);
     }
     
     @Override
     public synchronized Object getObject(Object key) {
         return delegate.getObject(key);
     }
     
     @Override
     public synchronized Object removeObject(Object key) {
         return delegate.removeObject(key);
     }
     
     @Override
     public synchronized void clear() {
         delegate.clear();
     }
     
     // 其他方法委托给delegate
 }

设计要点:所有修改操作都需要同步,但同步粒度较粗,适合并发量不高的场景。

2. LoggingCache:缓存命中率监控
java 复制代码
 public class LoggingCache implements Cache {
     private final Cache delegate;
     private int hits = 0;
     private int requests = 0;
     
     public LoggingCache(Cache delegate) {
         this.delegate = delegate;
     }
     
     @Override
     public Object getObject(Object key) {
         requests++;
         Object value = delegate.getObject(key);
         if (value != null) {
             hits++;
         }
         
         if (requests % 100 == 0) { // 每100次请求输出日志
             logHitRatio();
         }
         return value;
     }
     
     private void logHitRatio() {
         double ratio = (double) hits / requests * 100;
         System.out.printf("缓存命中率: %.2f%% (命中: %d, 总请求: %d)%n", 
                          ratio, hits, requests);
     }
 }

监控价值:通过命中率分析缓存效果,指导缓存策略优化。

3. SerializedCache:对象序列化与副本保护

这是二级缓存中最关键的装饰器,解决对象共享的核心问题:

java 复制代码
public class SerializedCache implements Cache {
     private final Cache delegate;
     
     public SerializedCache(Cache delegate) {
         this.delegate = delegate;
     }
     
     @Override
     public void putObject(Object key, Object value) {
         if (value == null || value instanceof Serializable) {
             delegate.putObject(key, serialize((Serializable) value));
         } else {
             throw new CacheException("缓存对象必须实现Serializable接口");
         }
     }
     
     @Override
     public Object getObject(Object key) {
         Object value = delegate.getObject(key);
         return value != null ? deserialize((byte[]) value) : null;
     }
     
     private byte[] serialize(Serializable value) {
         try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
              ObjectOutputStream oos = new ObjectOutputStream(bos)) {
             oos.writeObject(value);
             return bos.toByteArray();
         } catch (IOException e) {
             throw new CacheException("序列化失败", e);
         }
     }
     
     private Serializable deserialize(byte[] value) {
         try (ByteArrayInputStream bis = new ByteArrayInputStream(value);
              ObjectInputStream ois = new ObjectInputStream(bis)) {
             return (Serializable) ois.readObject();
         } catch (Exception e) {
             throw new CacheException("反序列化失败", e);
         }
     }
 }

为什么必须使用SerializedCache?

  1. 对象隔离:不同SqlSession获取的是不同对象实例,避免并发修改冲突

  2. 深度复制:确保缓存对象的完整性,防止浅拷贝带来的数据不一致

  3. 跨JVM支持:为分布式缓存奠定基础

4. LruCache:内存资源智能管理
java 复制代码
 public class LruCache implements Cache {
     private final Cache delegate;
     private final LinkedHashMap<Object, Object> keyMap;
     private Object eldestKey;
     
     public LruCache(Cache delegate, final int size) {
         this.delegate = delegate;
         this.keyMap = new LinkedHashMap<Object, Object>(size, 0.75f, true) {
             @Override
             protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
                 boolean tooBig = size() > size;
                 if (tooBig) {
                     eldestKey = eldest.getKey();
                 }
                 return tooBig;
             }
         };
     }
     
     @Override
     public void putObject(Object key, Object value) {
         delegate.putObject(key, value);
         cycleKeyList(key);
     }
     
     @Override
     public Object getObject(Object key) {
         keyMap.get(key); // 触发访问顺序更新
         return delegate.getObject(key);
     }
     
     private void cycleKeyList(Object key) {
         keyMap.put(key, key);
         if (eldestKey != null) {
             delegate.removeObject(eldestKey);
             eldestKey = null;
         }
     }
 }

LRU算法精髓:通过LinkedHashMap的访问顺序特性,自动维护最近使用顺序。

5. BlockingCache:防止缓存击穿的保护盾
java 复制代码
 public class BlockingCache implements Cache {
     private final Cache delegate;
     private final ConcurrentHashMap<Object, ReentrantLock> locks;
     
     public BlockingCache(Cache delegate) {
         this.delegate = delegate;
         this.locks = new ConcurrentHashMap<>();
     }
     
     @Override
     public Object getObject(Object key) {
         acquireLock(key);
         try {
             return delegate.getObject(key);
         } finally {
             releaseLock(key);
         }
     }
     
     @Override
     public void putObject(Object key, Object value) {
         try {
             delegate.putObject(key, value);
         } finally {
             releaseLock(key);
         }
     }
     
     private void acquireLock(Object key) {
         Lock lock = locks.computeIfAbsent(key, k -> new ReentrantLock());
         lock.lock();
     }
     
     private void releaseLock(Object key) {
         Lock lock = locks.get(key);
         if (lock != null) {
             lock.unlock();
             locks.remove(key); // 清理空闲锁
         }
     }
 }

防击穿原理:当多个线程同时查询同一个不存在的数据时,只有一个线程会访问数据库,其他线程等待结果。

三、CacheBuilder:装饰器的灵活组合器

java 复制代码
 public class CacheBuilder {
     private Cache delegate;
     
     public CacheBuilder(Cache delegate) {
         this.delegate = new PerpetualCache("default");
     }
     
     public CacheBuilder size(int size) {
         this.delegate = new LruCache(delegate, size);
         return this;
     }
     
     public CacheBuilder blocking() {
         this.delegate = new BlockingCache(delegate);
         return this;
     }
     
     public CacheBuilder serialized() {
         this.delegate = new SerializedCache(delegate);
         return this;
     }
     
     public CacheBuilder logging() {
         this.delegate = new LoggingCache(delegate);
         return this;
     }
     
     public CacheBuilder synchronized() {
         this.delegate = new SynchronizedCache(delegate);
         return this;
     }
     
     public Cache build() {
         return delegate;
     }
 }
 ​
 // 使用示例
 Cache cache = new CacheBuilder(new PerpetualCache("userCache"))
     .size(1000)
     .serialized()
     .blocking()
     .logging()
     .build();

建造者模式优势:通过链式调用,直观地组合各种缓存特性。

四、装饰器执行顺序的架构考量

装饰器的包装顺序直接影响缓存行为:

正确的顺序设计
java 复制代码
 // 推荐顺序:从内到外
 Cache cache = new LoggingCache(           // 最外层:监控统计
                  new BlockingCache(       // 防击穿保护
                  new SynchronizedCache(   // 线程安全
                  new SerializedCache(     // 序列化
                  new LruCache(            // 淘汰策略
                  new PerpetualCache()     // 基础存储
                  )))));
顺序设计原则
  1. 基础功能在内层:PerpetualCache作为存储核心

  2. 数据转换靠近核心:SerializedCache应在内层确保数据格式统一

  3. 并发控制在中层:SynchronizedCache/BlockingCache控制并发访问

  4. 监控统计在外层:LoggingCache最后包装以统计完整链路

五、生产环境中的装饰器实践

1. 性能敏感场景的优化
java 复制代码
 // 高并发读场景:使用读写锁替代同步锁
 public class ReadWriteCache implements Cache {
     private final Cache delegate;
     private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
     
     @Override
     public Object getObject(Object key) {
         rwLock.readLock().lock();
         try {
             return delegate.getObject(key);
         } finally {
             rwLock.readLock().unlock();
         }
     }
 }
2. 分布式缓存集成
java 复制代码
 // Redis缓存装饰器
 public class RedisCache implements Cache {
     private final JedisPool jedisPool;
     private final String namespace;
     
     @Override
     public void putObject(Object key, Object value) {
         try (Jedis jedis = jedisPool.getResource()) {
             jedis.setex(buildRedisKey(key), expireTime, serialize(value));
         }
     }
 }
3. 缓存预热策略
java 复制代码
 public class WarmUpCache implements Cache {
     private final Cache delegate;
     private final ScheduledExecutorService scheduler;
     
     public void scheduleWarmUp() {
         scheduler.scheduleAtFixedRate(this::warmUp, 0, 30, TimeUnit.MINUTES);
     }
 }

六、装饰器模式的架构思想延伸

装饰器模式的价值不仅限于缓存实现,它体现了重要的软件设计原则:

  1. 单一职责原则:每个装饰器只关注一个特定功能

  2. 开闭原则:新增功能不需要修改现有代码

  3. 组合优于继承:通过组合实现功能的灵活扩展

这种设计思想可以应用到:

  • 日志系统的Appender装饰

  • 网络连接的Filter链

  • 业务逻辑的Interceptor栈

七、总结

MyBatis二级缓存的装饰器体系展示了优秀架构设计的魅力。通过合理的装饰器组合,可以构建出功能强大、性能优越、可维护性高的缓存系统。

关键收获

  • 装饰器模式实现了功能的正交组合

  • SerializedCache解决了对象共享的核心问题

  • BlockingCache有效防止缓存击穿

  • CacheBuilder提供了优雅的配置方式

  • 装饰器顺序影响系统行为和性能

在实际项目中,应根据具体场景选择合适的装饰器组合,并通过监控数据持续优化缓存策略。


🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞

💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论

🔥🔥🔥(源码 + 调试运行 + 问题答疑)

🔥🔥🔥 有兴趣可以联系我。文末有免费源码

💖学习知识需费心,
📕整理归纳更费神。
🎉源码免费人人喜,
🔥码农福利等你领!

💖常来我家多看看,
📕网址:
扣棣编程** ,
🎉感谢支持常陪伴,
🔥点赞关注别忘记!**

💖山高路远坑又深,
📕大军纵横任驰奔,
🎉谁敢横刀立马行?
🔥唯有点赞+关注成!

往期文章推荐:

基于Springboot + vue实现的学生宿舍信息管理系统
免费获取宠物商城源码--SpringBoot+Vue宠物商城网站系统
【2025小年源码免费送】

⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇点击此处获取源码⬇⬇⬇⬇⬇⬇⬇⬇⬇

相关推荐
凯尔萨厮2 小时前
Java学习笔记五(多态)
java·笔记·学习
迷迷的k2 小时前
后端两个接口需分开写,前端需不串行并同时刷新调用但数据不同步NOTE
java·数据同步问题
AI+程序员在路上2 小时前
QT中QStackedWidget控件功能及应用
开发语言·qt
轩情吖2 小时前
Qt常用控件之QTextEdit
开发语言·c++·qt·信号·qtextedit·多行输入框·桌面级开发
无敌最俊朗@2 小时前
Qt Model/View/Delegate 架构深度解析
开发语言·qt·架构
xiaoxiao无脸男3 小时前
three.js
开发语言·前端·javascript
hnlgzb3 小时前
安卓中,kotlin如何写app界面?
android·开发语言·kotlin
一叶飘零_sweeeet3 小时前
极简 Go 语言教程:从 Java 开发者视角 3 小时入门实战
java·开发语言·golang
BUTCHER53 小时前
Go语言环境安装
linux·开发语言·golang