基于MyBatis二级缓存深入装饰器模式

视频地址

学习文档


文章目录

  • 一、示意代码
  • 二、装饰器
  • 三、经典案例—MyBatis二级缓存
    • 1、Cache 标准定义
    • 2、PerpetualCache 基础实现
    • 3、增强实现
      • 3-1、ScheduledCache
      • 3-2、LruCache


先来说说我对装饰器理解:当你有一个基础功能的代码,但你想在不改变原来代码的基础上进行功能增强,并且可以随意组合增强的时候,就可以使用装饰器模式。

我把装饰器模式理解成是一种套娃模式,所谓套娃就是一层套一层,随意组合想怎么套就怎么套,层层嵌套的基础是有一个最初的原型。


一、示意代码


对于个人而言,首先是一个人,其次可以有不同的身份,各种身份可以随意组合。这就可以看成一个装饰器模式的案例。

// 定义接口
interface Human {String identity();
}// 基础实现
class OrdinaryHuman implements Human {@Overridepublic String identity() {return "我是一个人";}
}// 增强实现
class Teacher implements Human {protected Human human;public teacher(Human human) {this.human = human;}@Overridepublic String identity() {human.identity();return "我是一名老师";}
}// 增强实现
class Father implements Human {protected Human human;public father(Human human) {this.human = human;}@Overridepublic String identity() {human.identity();return "我是一名父亲";}
}
public static void main(String[] args) {OrdinaryHuman ordinaryHuman = new OrdinaryHuman();Teacher teacher = new Teacher(ordinaryHuman);Father father = new Father(teacher);father.identity();
}

输出

我是一个人
我是一名老师
我是一名父亲

二、装饰器


GPT的解释
装饰器模式是一种能够在不改变原对象代码的情况下,动态地为对象添加新功能的设计模式。通过将对象包装在装饰器类中,可以透明地、在运行时选择性地、以任意顺序地应用这些功能。最终效果是通过组合不同的装饰器,扩展原对象功能,使系统更灵活可扩展。


想要实现一个装饰器,如下三步:

  1. 要有一个标准定义(Human)
  2. 要有一个基础的实现(OrdinaryHuman)
  3. 每个增强的的实现除了实现标准定义重写对应的方法之外,还要提供一个特殊的构造方法,参数就是接口的某个实现类(基于这个特殊的构造方法,就可以随意组合了)

三、经典案例—MyBatis二级缓存


在这里插入图片描述


  1. Cache 标准定义
  2. PerpetualCache 基础实现
  3. decorators 包下面的就是各种增强实现

注:在现在分布式系统的大环境下,MyBatis的二级缓存几乎没有用武之地,但它确实理解装饰器模式的一个好案例,它的代码很简单,所以格外的好理解


1、Cache 标准定义


cache里面定义了缓存的一些操作

public interface Cache {String getId();void putObject(Object var1, Object var2);Object getObject(Object var1);Object removeObject(Object var1);void clear();int getSize();default ReadWriteLock getReadWriteLock() {return null;}
}

2、PerpetualCache 基础实现


PerpetualCache 的实现也很简单,就是一个 HashMap,Value就是缓存的数据,Key 是自定义的 CacheKey

public class PerpetualCache implements Cache {private final String id;private Map<Object, Object> cache = new HashMap();public PerpetualCache(String id) {this.id = id;}public String getId() {return this.id;}public int getSize() {return this.cache.size();}public void putObject(Object key, Object value) {this.cache.put(key, value);}public Object getObject(Object key) {return this.cache.get(key);}public Object removeObject(Object key) {return this.cache.remove(key);}public void clear() {this.cache.clear();}public boolean equals(Object o) {if (this.getId() == null) {throw new CacheException("Cache instances require an ID.");} else if (this == o) {return true;} else if (!(o instanceof Cache)) {return false;} else {Cache otherCache = (Cache)o;return this.getId().equals(otherCache.getId());}}public int hashCode() {if (this.getId() == null) {throw new CacheException("Cache instances require an ID.");} else {return this.getId().hashCode();}}
}

CacheKey 并不是装饰器的里面内容,但它的设计挺有意思的,简单来说就是:把当前这次操作的标识,比如 接口全限定名、请求参数、执行器信息 存入 List里面, 基于这些信息去生成唯一的 HashCode

public class CacheKey implements Cloneable, Serializable {private static final long serialVersionUID = 1146682552656046210L;public static final CacheKey NULL_CACHE_KEY = new CacheKey() {public void update(Object object) {throw new CacheException("Not allowed to update a null cache key instance.");}public void updateAll(Object[] objects) {throw new CacheException("Not allowed to update a null cache key instance.");}};private static final int DEFAULT_MULTIPLIER = 37;private static final int DEFAULT_HASHCODE = 17;private final int multiplier;private int hashcode;private long checksum;private int count;private List<Object> updateList;public CacheKey() {this.hashcode = 17;this.multiplier = 37;this.count = 0;this.updateList = new ArrayList();}public CacheKey(Object[] objects) {this();this.updateAll(objects);}public int getUpdateCount() {return this.updateList.size();}public void update(Object object) {int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);++this.count;this.checksum += (long)baseHashCode;baseHashCode *= this.count;this.hashcode = this.multiplier * this.hashcode + baseHashCode;this.updateList.add(object);}public void updateAll(Object[] objects) {Object[] var2 = objects;int var3 = objects.length;for(int var4 = 0; var4 < var3; ++var4) {Object o = var2[var4];this.update(o);}}public boolean equals(Object object) {if (this == object) {return true;} else if (!(object instanceof CacheKey)) {return false;} else {CacheKey cacheKey = (CacheKey)object;if (this.hashcode != cacheKey.hashcode) {return false;} else if (this.checksum != cacheKey.checksum) {return false;} else if (this.count != cacheKey.count) {return false;} else {for(int i = 0; i < this.updateList.size(); ++i) {Object thisObject = this.updateList.get(i);Object thatObject = cacheKey.updateList.get(i);if (!ArrayUtil.equals(thisObject, thatObject)) {return false;}}return true;}}}public int hashCode() {return this.hashcode;}public String toString() {StringJoiner returnValue = new StringJoiner(":");returnValue.add(String.valueOf(this.hashcode));returnValue.add(String.valueOf(this.checksum));Stream var10000 = this.updateList.stream().map(ArrayUtil::toString);Objects.requireNonNull(returnValue);var10000.forEach(returnValue::add);return returnValue.toString();}public CacheKey clone() throws CloneNotSupportedException {CacheKey clonedCacheKey = (CacheKey)super.clone();clonedCacheKey.updateList = new ArrayList(this.updateList);return clonedCacheKey;}
}

3、增强实现


它这个里面的增强实现挺多的,着重看两个有意思的 ScheduledCache、LruCache


3-1、ScheduledCache


ScheduledCache 是一个支持定时清除缓存数据,可以通过 setClearInterval 方法设置清除时间缓存间隔,单位毫秒。

public void setClearInterval(long clearInterval) {this.clearInterval = clearInterval;
}

清除缓存的原理并没有很复杂,它的代码超级简单, this.lastClear 是上一次清除的时间,只需要在操作缓存的之前先调用下面的代码即可。

private boolean clearWhenStale() {if (System.currentTimeMillis() - this.lastClear > this.clearInterval) {this.clear();return true;} else {return false;}
}

全部代码如下:

public class ScheduledCache implements Cache {private final Cache delegate;protected long clearInterval;protected long lastClear;public ScheduledCache(Cache delegate) {this.delegate = delegate;this.clearInterval = TimeUnit.HOURS.toMillis(1L);this.lastClear = System.currentTimeMillis();}public void setClearInterval(long clearInterval) {this.clearInterval = clearInterval;}public String getId() {return this.delegate.getId();}public int getSize() {this.clearWhenStale();return this.delegate.getSize();}public void putObject(Object key, Object object) {this.clearWhenStale();this.delegate.putObject(key, object);}public Object getObject(Object key) {return this.clearWhenStale() ? null : this.delegate.getObject(key);}public Object removeObject(Object key) {this.clearWhenStale();return this.delegate.removeObject(key);}public void clear() {this.lastClear = System.currentTimeMillis();this.delegate.clear();}public int hashCode() {return this.delegate.hashCode();}public boolean equals(Object obj) {return this.delegate.equals(obj);}private boolean clearWhenStale() {if (System.currentTimeMillis() - this.lastClear > this.clearInterval) {this.clear();return true;} else {return false;}}
}

3-2、LruCache


GPT对LRU的解释
LRU(Least Recently Used)是一种缓存淘汰策略,用于管理缓存中的数据项。该策略的基本思想是,当缓存达到容量上限时,选择最近最少被使用的数据项进行淘汰。


LruCache 的代码很少,来一起看看它是如何实现LUR的

  1. eldestKey 最少被使用的一个数据,当超过容量的时候就删除它
  2. 每次添加元素的时候都可能超容,通过 cycleKeyList 方法判断是否需要删除数据
  3. LinkedHashMap 提供了一个删除最后一个元素的接口 removeEldestEntry ,通过它来删除最后一个元素
public class LruCache implements Cache {private final Cache delegate;private Map<Object, Object> keyMap;private Object eldestKey;public LruCache(Cache delegate) {this.delegate = delegate;this.setSize(1024);}public String getId() {return this.delegate.getId();}public int getSize() {return this.delegate.getSize();}public void setSize(final int size) {this.keyMap = new LinkedHashMap<Object, Object>(size, 0.75F, true) {private static final long serialVersionUID = 4267176411845948333L;// eldest 就是链表中最后一个元素// 当容量大小超过的时候就让 LruCache.this.eldestKey = eldest.getKey();protected boolean removeEldestEntry(Entry<Object, Object> eldest) {boolean tooBig = this.size() > size;if (tooBig) {LruCache.this.eldestKey = eldest.getKey();}return tooBig;}};}public void putObject(Object key, Object value) {this.delegate.putObject(key, value);this.cycleKeyList(key);}public Object getObject(Object key) {this.keyMap.get(key);return this.delegate.getObject(key);}public Object removeObject(Object key) {return this.delegate.removeObject(key);}public void clear() {this.delegate.clear();this.keyMap.clear();}private void cycleKeyList(Object key) {this.keyMap.put(key, key);// 如果当前要删除的元素不为空,就删除这个元素if (this.eldestKey != null) {this.delegate.removeObject(this.eldestKey);this.eldestKey = null;}}
}

基于上面的分析,只需要来看看 removeEldestEntry 方法是在什么时候调用的就好了,往LinkedHashMap添加元素的时候,调用的实际上是 HashMap 的 put方法

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}

省去插入元素和扩容的代码,调用了 afterNodeInsertion方法 evict = true

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {// ....afterNodeInsertion(evict);return null;
}

LinkedHashMap中 afterNodeInsertion 实现,把链表头也是最开始的元素传递给 removeEldestEntry

void afterNodeInsertion(boolean evict) { // possibly remove eldestLinkedHashMap.Entry<K,V> first;if (evict && (first = head) != null && removeEldestEntry(first)) {K key = first.key;removeNode(hash(key), key, null, false, true);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/220553.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高效营销系统集成:百度营销的API无代码解决方案,提升电商与广告效率

百度营销API连接&#xff1a;构建无代码开发的高效集成体系 在数字营销的高速发展时代&#xff0c;企业追求的是快速响应市场的能力以及提高用户运营的效率。百度营销API连接正是为此而生&#xff0c;它通过无代码开发的方式&#xff0c;实现了电商平台、营销系统和CRM的一站式…

墒情监测FDS-400 土壤温湿电导率盐分传感器

墒情监测FDS-400 土壤温湿电导率盐分传感器产品概述 土壤温度部分是由精密铂电阻和高精度变送器两部分组成。变送器部分由电源模块、温度传感模块、变送模块、温度补偿模块及数据处理模块等组成&#xff0c;解决铂电阻因自身特点导入的测量误差&#xff0c;变送器内有零漂电路…

Redis队列原理解析:让你的应用程序运行更加稳定!

一、消息队列简介 消息队列&#xff08;Message Queue&#xff09;&#xff0c;字面意思就是存放消息的队列。最简单的消息队列模型包括 3 个角色&#xff1a; 消息队列&#xff1a;存储和管理消息&#xff0c;也被称为消息代理&#xff08;Message Broker&#xff09;生产者…

Turtle绘制菱形-第11届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第16讲。 Turtle绘制菱形&a…

六.聚合函数

聚合函数 1.什么是聚合函数1.1AVG和SUM函数1.2MIN和MAX函数1.3COUNT函数 2.GROUP BY2.1基本使用2.2使用多个列分组2.3GROUP BY中使用WITH ROLLUP 3.HAVING3.1基本使用3.2WHERE和HAVING的区别 4.SELECT的执行过程4.1查询的结构4.2SELECT执行顺序4.3SQL执行原理 1.什么是聚合函数…

用友 U8总账凭证打印设置

总账--凭证打印——设置 是设置凭证打印显示的格子框&#xff0c;勾上就有框&#xff0c;去掉就没有框。

判断css文字发生了截断,增加悬浮提示

示例&#xff1a; 固定显示宽度&#xff0c;溢出显示...&#xff0c;利用了css的属性&#xff0c;想要实现成下面这样&#xff1a; 针对溢出的文字&#xff0c;hover显示全部。 提示很好加&#xff0c;使用tooltip组件就行了&#xff0c;难点是如何判断是否发生了文字溢出。…

JS数组与它的42个方法

前言 数组在js中作为一个非常重要的类型之一&#xff0c;在我们对数据处理&#xff0c;存储数据&#xff0c;条件渲染的时候经常会用到&#xff0c;所以随着ES的不断更新&#xff0c;数组的方法也是越来越多&#xff0c;也让我们使用数组对数据操作的时候&#xff0c;越来越简…

竞赛保研 python 爬虫与协同过滤的新闻推荐系统

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python 爬虫与协同过滤的新闻推荐系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 该项目较为新颖&…

Python求小于m的最大10个素数

为了找到小于m的最大10个素数&#xff0c;我们首先需要确定m的值。然后&#xff0c;我们可以使用一个简单的算法来检查每一个小于m的数字是否是素数。 下面是一个Python代码示例&#xff0c;可以找到小于m的最大10个素数&#xff1a; def is_prime(n): if n < 1: …

Conda 使用教程大全来啦

什么是 Conda&#xff1f; Conda 是一款功能强大的软件包管理器和环境管理器&#xff0c;您可以在 Windows 的 Anaconda 提示符或 macOS 或 Linux 的终端窗口中使用命令行命令 Conda 可以快速安装、运行和更新软件包及相关依赖项。Conda 可以在本地计算机上创建、保存、加载和…

swing快速入门(八)

注释很详细&#xff0c;直接上代码 上一篇 新增内容 cardLayout布局管理器 事件监听器的创建与绑定 多种布局与容器的结合使用 import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;public class swing_test_6 {public static v…

佛山数字孪生赋能工业智能制造,助力制造业企业数字化转型

佛山数字孪生赋能工业智能制造&#xff0c;助力制造业企业数字化转型。数字孪生驱动的仿真服务可以模拟产品的各种真实功能&#xff0c;为不同的用户切换不同的应用场景。产品介绍、咨询和体验服务都可以通过产品数字孪生来完成。产品数字孪生在交易时可以交付给客户。产品销售…

【ARM Trace32(劳特巴赫) 使用介绍 14 -- Go.direct 介绍】

请阅读【Trace32 ARM 专栏导读】 文章目录 Trace32 Go.directGo配合程序断点使用Go 配合读写断点使用Go 快速回到上一层函数 System.Mode Go Trace32 Go.direct TRACE32调试过程中&#xff0c;会经常对芯片/内核进行控制&#xff0c;比如全速运行、暂停、单步等等。这篇文章先…

Go——协程

协程 协程是Go语言最大的特色之一。 1、协程的概念 协程并不是Go发明的概念&#xff0c;支持协程的变成语言有很多。Go在语言层面直接提供对协程的支持称为goroutine。 1.1 基本概念 进程 进程是应用程序启动的实例&#xff0c;每个进程都有独立的内存空间&#xff0c;不同…

记录 | vscode禁止插件自动更新的方法

shift command p 打开然后输入 > setting.json&#xff0c;选择用户设置 在 settings.json 配置文件中增加一项&#xff1a; "extensions.autoUpdate": false,

ohpm : 无法将“ohpm”项识别为 cmdlet、函数...

这是因为没有在环境变量里配置 Ohpm. 左上角File->Settings,找到Ohpm放的路径 bin目录下&#xff0c;然后复制 此电脑->右键属性->高级系统设置->环境变量->系统变量找到Path,添加刚才复制的那一行 重启 DevEco ,在Terminal输入 ohpm -v ,出现版本号就欧了 如果…

Python中容易被忽视的核心功能

Python是一门富有魅力的编程语言&#xff0c;拥有丰富的功能和库&#xff0c;以及强大的社区支持。然而&#xff0c;有一些核心功能经常被忽视&#xff0c;而它们实际上可以极大地提高代码的质量、可读性和性能。 1. 解析命令行参数的argparse库 很多Python开发者在编写命令行…

开关电源测试之电源漏电流测试方法分享

一、外观检测 检查开关电源外观是否完好&#xff0c;是否有破损、变形、漏油等情况。 二、检测火线和零线的电流 实时测量火线和零线的电流&#xff0c;当两个电流值不相等且都不为零时断开零线&#xff0c;然后测火线的电流。当火线电流不为0时&#xff0c;判断电流为漏电流状…

02.尚医通 Mybatis-Plus

1、前期准备 a. 创建数据库 CREATE TABLE USER (id BIGINT(20)NOT NULL COMMENT 主键ID,NAME VARCHAR(30)NULL DEFAULT NULL COMMENT 姓名,age INT(11)NULL DEFAULT NULL COMMENT 年龄,email VARCHAR(50)NULL DEFAULT NULL COMMENT 邮箱,PRIMARY KEY (id) );INSERT INTO user…