一、ExpiringMap概述
ExpiringMap项目地址:https://github.com/jhalterman/expiringmap
1、ExpiringMap简介
针对一些小体量的项目,存储的数据量也不是很大(如校验码)的情况下,使用 Redis会增加系统的复杂性和维护难度。
ExpiringMap就是一个轻量的Java缓存方式,它的功能和Redis类似。
ExpiringMap 是一个具有高性能,低开销,零依赖,由线程安全的 ConcurrentMap 实现过期 entries 等优点的 Map。
主要特点包括:过期策略、可变有效期、最大尺寸、侦听器过期、延迟输入加载、过期自省等。
ExpiringMap基本功能包括:
- 可设置 Map 中的 Entry 在一段时间后自动过期。
- 可设置 Map 最大容纳值,当到达 Maximum size 后,再次插入值会导致 Map 中的第一个值过期。
- 可添加监听事件,在监听到 Entry 过期时调度监听函数。
- 可以设置懒加载,在调用 get() 方法时创建对象。
2、ExpiringMap常用API
enum ExpirationPolicy
:枚举,确定 ExpiringMap 元素应如何过期,- ACCESSED 根据上次访问的时间使它们过期,即从上次访问后开始计时;
- CREATED 根据条目的创建时间使它们过期,即从创建之后开始计时。
- enum TimeUnit:枚举,确定时间单位,MILLISECONDS 时间单位毫秒,SECONDS 时间单位秒,MINUTES 时间单位分钟,HOURS 时间单位小时,DAYS 时间单位天
- interface EntryLoader<K, V>:按需加载,内有 V load(K key) 方法,调用时将 key 的新值加载到即将过期的映射中。
- interface ExpiringEntryLoader<K, V>:按需加载,并控制每个值的过期时间(即可变过期时间),内有 ExpiringValue load(K key) 方法,调用时将 key 的新值加载到即将过期的映射中。
- static final class Builder<K, V>:内部类,ExpiringMap 构造器。 默认为 ExpirationPolicy.CREATED,60 个 TimeUnit.SECONDS 到期和 Integer.MAX_VALUE 的 MAXSIZE。
static Builder<Object, Object> builder()
:创建一个 ExpiringMap 构造器,返回 ExpiringMap.Builder。- Builder<K, V> expiration(long duration, TimeUnit timeUnit):构造器中的方法,用于设置 Map 的过期时间,返回 ExpiringMap.Builder。- Builder<K, V> maxSize(int maxSize):设置 Map 的最大个数,超过限制仍要添加元素时将最先过期的元素过期。
- int getMaxSize():获取 Map 的最大个数。
- Builder<K1, V1> entryLoader(EntryLoader<? super K1, ? super V1> loader):构造器中的方法,可以设置 EntryLoader,EntryLoader 和 ExpiringEntryLoader,不能同时设置。
- Builder<K1, V1> expiringEntryLoader(ExpiringEntryLoader<? super K1, ? super V1> loader):构造器中的方法,可以设置 ExpiringEntryLoader,EntryLoader 和 ExpiringEntryLoader,不能同时设置。
- Builder<K, V> expirationPolicy(ExpirationPolicy expirationPolicy):构造器中的方法,用于设置 Map 的过期策略,返回 ExpiringMap.Builder。
Builder<K, V> variableExpiration()
:构造器中的方法,允许 Map 元素具有各自的到期时间,并允许更改到期时间。Builder<K1, V1> expirationListener(ExpirationListener<? super K1, ? super V1> listener)
:构造器中的方法,配置监听每个 Map 元素过期, 通知传入的 ExpirationListener 执行预定好的操作。- void addExpirationListener(ExpirationListener<K, V> listener):给 ExpiringMap 添加过期监听器。
- void removeExpirationListener(ExpirationListener<K, V> listener):移出 ExpiringMap 的过期监听。
- long getExpiration(K key):获取指定 key 对应的元素的过期时间,返回毫秒值。
- long getExpectedExpiration(K key):获取指定 key 预计到期时间,返货毫秒值。
- ExpirationPolicy getExpirationPolicy(K key):获取指定 key 对应的元素的过期策略。
- interface ExpirationListener<K, V>:过期监听接口,有一个 void expired(K key, V value) 方法,过期时自动调用。
- ExpiringMap<K1, V1> build():创建并返回一个 ExpiringMap。
二、使用示例
使用 ExpiringMap之前,需要引入 pom依赖。
<!-- expiringmap依赖 -->
<dependency><groupId>net.jodah</groupId><artifactId>expiringmap</artifactId><version>0.5.11</version>
</dependency>
1、构建时设置过期时间与过期策略
/*** 初始化一个ExpiringMap(配置过期时间、过期协议等)*/private final static ExpiringMap<String, String> expireCacheMap = ExpiringMap.builder()// 设置最大值,添加第11个entry时,会导致第1个立马过期(即使没到过期时间)。默认 Integer.MAX_VALUE.maxSize(10)// 允许 Map 元素具有各自的到期时间,并允许更改到期时间。.variableExpiration()// 设置过期时间,如果key不设置过期时间,key永久有效。.expiration(10, TimeUnit.SECONDS).asyncExpirationListener((key, value) -> {log.info("expireCacheMap key数据被删除了 -> key={}, value={}", key, value);})//设置 Map 的过期策略.expirationPolicy(ExpirationPolicy.ACCESSED).build();public static void main(String[] args) throws InterruptedException {expireCacheMap.put("token", UUID.randomUUID().toString());// 模拟线程等待TimeUnit.SECONDS.sleep(8);String token = expireCacheMap.get("token");long expiration = expireCacheMap.getExpiration("token");long expectedExpiration = expireCacheMap.getExpectedExpiration("token");log.info("token ===> " + token);log.info("设置的过期时间 ===> " + expiration / 1000);log.info("剩余过期时间 ===> " + expectedExpiration / 1000);// 模拟线程等待TimeUnit.SECONDS.sleep(10);log.info("token ===> " + expireCacheMap.get("token"));TimeUnit.SECONDS.sleep(60);}
说明一下:
- maxSize:Map存储的最大值,类似队列,容量固定,当操作map容量超出限制时,最开始的元素就会依次过期,只保留最新的;
- expirationPolicy:过期策略,包括 ExpirationPolicy.ACCESSED 和 ExpirationPolicy.CREATED 两种;
- 1)ExpirationPolicy.ACCESSED :每进行一次访问,过期时间就会自动清零,重新计算;
- 2)ExpirationPolicy.CREATED:在过期时间内重新 put 值的话,过期时间会清理,重新计算;默认策略。
2、单个元素设置过期时间
/*** 构建后给单个元素设置过期时间*/private final static ExpiringMap<String, String> expireCacheMap2 = ExpiringMap.builder()// 设置最大值,添加第11个entry时,会导致第1个立马过期(即使没到过期时间)。默认 Integer.MAX_VALUE.maxSize(10)// 允许 Map 元素具有各自的到期时间,并允许更改到期时间。.variableExpiration().asyncExpirationListener((key, value) -> {log.info("expireCacheMap2 key数据被删除了 -> key={}, value={}", key, value);})//设置 Map 的过期策略.expirationPolicy(ExpirationPolicy.ACCESSED).build();public static void main(String[] args) throws InterruptedException {expireCacheMap2.put("token", UUID.randomUUID().toString(), ExpirationPolicy.ACCESSED, 30, TimeUnit.SECONDS);expireCacheMap2.put("token2", UUID.randomUUID().toString(), ExpirationPolicy.CREATED, 30, TimeUnit.SECONDS);// 模拟线程等待TimeUnit.SECONDS.sleep(10);log.info("token ===> " + expireCacheMap2.get("token"));log.info("设置的过期时间 ===> " + expireCacheMap2.getExpiration("token") / 1000);log.info("剩余过期时间 ===> " + expireCacheMap2.getExpectedExpiration("token") / 1000);log.info("token2 ===> " + expireCacheMap2.get("token2"));log.info("设置的过期时间 ===> " + expireCacheMap2.getExpiration("token2") / 1000);log.info("剩余过期时间 ===> " + expireCacheMap2.getExpectedExpiration("token2") / 1000);// 模拟线程等待TimeUnit.SECONDS.sleep(20);log.info("token ===> " + expireCacheMap2.get("token"));log.info("token2 ===> " + expireCacheMap2.get("token2"));TimeUnit.SECONDS.sleep(60);}
说明一下:
- variableExpiration:允许 Map 元素具有各自的到期时间,并允许更改到期时间。也就是说可以设置单个元素的过期时间。
3、设置懒加载
@Data
public class StudentDTO implements Serializable {private static final long serialVersionUID = 3220190006057051497L;private String name;private Integer age;
}
/*** 构建后给元素设置懒加载。适合value为引用对象*/private final static ExpiringMap<String, StudentDTO> expireCacheMap3 = ExpiringMap.builder().expiration(10, TimeUnit.SECONDS)//设置懒加载.entryLoader(key -> new StudentDTO()).expirationPolicy(ExpirationPolicy.ACCESSED).build();private final static ExpirationListener<String, StudentDTO> expirationListener = (key, value) -> log.info("expireCacheMap3 key数据被删除了 -> key={}, value={}", key, value);public static void main(String[] args) throws InterruptedException {// 构建时设置过期监听,或者添加过期监听。expireCacheMap3.addExpirationListener(expirationListener);// 使用懒加载时,可以不用去 put 对象,在调用 get 方法时会自动创建对象StudentDTO studentDTO1 = expireCacheMap3.get("studentDTO1");studentDTO1.setName("赵云");studentDTO1.setAge(17);log.info("studentDTO1 ===> " + expireCacheMap3.get("studentDTO1"));TimeUnit.SECONDS.sleep(3);log.info("token ===> " + expireCacheMap3.get("studentDTO1"));log.info("设置的过期时间 ===> " + expireCacheMap3.getExpiration("studentDTO1") / 1000);log.info("剩余过期时间 ===> " + expireCacheMap3.getExpectedExpiration("studentDTO1") / 1000);// 使用懒加载时,可以不用去 put 对象,在调用 get 方法时会自动创建对象StudentDTO studentDTO2 = expireCacheMap3.get("studentDTO2");studentDTO2.setName("赵子龙");studentDTO2.setAge(18);log.info("studentDTO2 ===> " + expireCacheMap3.get("studentDTO2"));TimeUnit.SECONDS.sleep(3);log.info("token ===> " + expireCacheMap3.get("studentDTO2"));log.info("设置的过期时间 ===> " + expireCacheMap3.getExpiration("studentDTO2") / 1000);log.info("剩余过期时间 ===> " + expireCacheMap3.getExpectedExpiration("studentDTO2") / 1000);// 模拟线程等待TimeUnit.SECONDS.sleep(20);log.info("studentDTO1 ===> " + expireCacheMap2.get("studentDTO1"));log.info("studentDTO2 ===> " + expireCacheMap2.get("studentDTO2"));TimeUnit.SECONDS.sleep(60);}
– 求知若饥,虚心若愚。