环境:
有些对外提供的工具模块不是很好引用reids依赖,需要使用本地缓存,本来行想省事直接使用hutool的超时缓存-TimedCache,但是这个工具是真的坑逼,到时间调用get方法还是能获取到数据(吸取教训,一些工具类大厂有的尽量优先使用大厂的工具类,再考虑hutool,当然hutool其他有些工具确实很方便)。
实现的功能:
(1)普通使用:缓存数据并配置有效时间,可设置默认时间自动清除缓存,也可以自己设置。
(2)进阶使用:除了到时自动清除外,还需要他执行其他操作,比如 文件分片上传功能,到时间自动执行取消上传接口,删除已经上传的部分数据,避免一直占用存储空间,此外要求单例。(生产环境不建议用这种方式,可以考虑用定时消息或者延时消息去做)
pom依赖
<!--工具类依赖-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.19</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
普通使用:
工具类
直接上代码:
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
public class TimeExpiredPoolCache {
private static long defaultCachedMillis = 10 * 60 * 1000L;//过期时间默认10分钟
private static long timerMillis = 10 * 60 * 1000L;//定时清理默认10分钟
/**
* 对象池
*/
private static ConcurrentHashMap<String, DataWrapper<?>> dataPool = null;
/**
* 对象单例
*/
private static TimeExpiredPoolCache instance = null;
private TimeExpiredPoolCache() {
dataPool = new ConcurrentHashMap<String, DataWrapper<?>>();
}
private static synchronized void syncInit() {
if (instance == null) {
instance = new TimeExpiredPoolCache();
initTimer();
}
}
public static TimeExpiredPoolCache getInstance() {
if (instance == null) {
syncInit();
}
return instance;
}
/**
* 定时器定时清理过期缓存
*/
private static Timer timer = new Timer();
private static void initTimer() {
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
clearExpiredCaches();
} catch (Exception e) {
//logger.error("clearExpiredCaches error.", e);
}
}
}, timerMillis, timerMillis);
}
/**
* 缓存数据
* @param key key值
* @param data 缓存数据
* @param cachedMillis 过期时间
* @param dataRenewer 刷新数据
* @return
*/
@SuppressWarnings("unchecked")
public <T> T put(String key, T data, long cachedMillis, DataRenewer<T> dataRenewer) throws Exception {
DataWrapper<T> dataWrapper = (DataWrapper<T>)dataPool.get(key);
if (data == null && dataRenewer != null) {
data = dataRenewer.renewData();
}
//当重新获取数据为空,直接返回不做put
if (data == null){
return null;
}
if (dataWrapper != null) {
//更新
dataWrapper.update(data, cachedMillis);
} else {
dataWrapper = new DataWrapper<T>(data, cachedMillis);
dataPool.put(key, dataWrapper);
}
return data;
}
/**
* 直接设置缓存值和时间
* @param key
* @param data
* @param cachedMillis
* @return
*/
@SuppressWarnings("unchecked")
public <T> T put(String key, T data, long cachedMillis) throws Exception{
DataWrapper<T> dataWrapper = (DataWrapper<T>)dataPool.get(key);
if (dataWrapper != null) {
//更新
dataWrapper.update(data, cachedMillis);
} else {
dataWrapper = new DataWrapper<T>(data, cachedMillis);
dataPool.put(key, dataWrapper);
}
return data;
}
/**
* 默认构造时间的缓存数据
* @param key
* @param data
* @param dataRenewer
* @return
*/
@Deprecated
public <T> T put(String key, T data, DataRenewer<T> dataRenewer) throws Exception {
return put(key, data, defaultCachedMillis, dataRenewer);
}
/**
* 获取缓存
* @param key
* @param cachedMillis
* @param dataRenewer
* @return
*/
@SuppressWarnings("unchecked")
public <T> T get(String key, long cachedMillis, DataRenewer<T> dataRenewer) throws Exception {
DataWrapper<T> dataWrapper = (DataWrapper<T>)dataPool.get(key);
if (dataWrapper != null && !dataWrapper.isExpired()) {
return dataWrapper.data;
}
return put(key, null, cachedMillis, dataRenewer);
}
@SuppressWarnings("unchecked")
public <T> T get(String key) throws Exception {
DataWrapper<T> dataWrapper = (DataWrapper<T>)dataPool.get(key);
if (dataWrapper != null && !dataWrapper.isExpired()) {
return dataWrapper.data;
}
return null;
}
/**
* 清除缓存
*/
public void clear() {
dataPool.clear();
}
/**
* 删除指定key的value
* */
public void remove(String key){
dataPool.remove(key);
}
/**
* 数据封装
*/
private class DataWrapper<T> {
/**
* 数据
*/
private T data;
/**
* 到期时间
*/
private long expiredTime;
/**
* 缓存时间
*/
private long cachedMillis;
private DataWrapper(T data, long cachedMillis) {
this.update(data, cachedMillis);
}
public void update(T data, long cachedMillis) {
this.data = data;
this.cachedMillis = cachedMillis;
this.updateExpiredTime();
}
public void updateExpiredTime() {
this.expiredTime = System.currentTimeMillis() + cachedMillis;
}
/**
* 数据是否过期
* @return
*/
public boolean isExpired() {
if (this.expiredTime > 0) {
return System.currentTimeMillis() > this.expiredTime;
}
return true;
}
}
/**
* 数据构造
*/
public interface DataRenewer<T> {
public T renewData();
}
/**
* 清除过期的缓存
*/
private static void clearExpiredCaches() {
List<String> expiredKeyList = new LinkedList<String>();
for(Entry<String, DataWrapper<?>> entry : dataPool.entrySet()){
if (entry.getValue().isExpired()) {
expiredKeyList.add(entry.getKey());
}
}
for (String key : expiredKeyList) {
dataPool.remove(key);
}
}
}
测试:
import com.example.demo2.utils.TimeExpiredPoolCache;
import org.springframework.web.bind.annotation.*;
@RestController
public class CacheDemo {
@RequestMapping(value = "/saveToken", method = RequestMethod.GET)
public String saveToken(){
try {
TimeExpiredPoolCache.getInstance().put("token","1008611",1000L*30);
new TimeThread().run();
return "保存token进入缓冲成功";
} catch (Exception e) {
return "保存token进入缓存失败";
}
}
@RequestMapping(value = "/getToken", method = RequestMethod.GET)
public String getToken(){
try {
// token = String.valueOf(TimeExpiredPoolCache.getInstance().get("token"));
String token = TimeExpiredPoolCache.getInstance().get("token");
if (null!=token){
return token;
}
} catch (Exception e) {
e.printStackTrace();
}
return "数据为空................";
}
public static class TimeThread implements Runnable{
@Override
public void run() {
for(int i=30;i>0;i--){
System.out.println("缓冲倒计时:"+i+" 秒");
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
浏览直接访问 (我这里设置的端口号是8888)
localhost:8888/saveToken 调用存储token值为1008611,时间为30秒有效
localhost:8888/getToken 可以获取token值,可以在缓存有效期内和有效期外分别调用,看下缓存值是否存在
进阶使用:
思路是传一个接口对象参数进去,比较简单,直接上代码
工具类:
import com.nw.test.service.CacheCleanService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 超时池缓存
*
* @author ppp
* @date 2022/10/9
*/
public class TimeCacheUtil {
/**
* 定时清理默认1天
*/
private static final long DEFAULT_TIMER_MILLIS = TimeUnit.DAYS.toMillis(1);
/**
* 对象池
*/
private static ConcurrentHashMap<String, DataWrapper<?>> dataPool = null;
/**
* 对象单例
*/
private static TimeCacheUtil instance = null;
/**
* 定时清理过期缓存定时器
*/
private static final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(3,
new BasicThreadFactory.Builder().namingPattern("cache-schedule-pool-%d").daemon(true).build());
private TimeCacheUtil() {
dataPool = new ConcurrentHashMap<String, DataWrapper<?>>();
}
private static synchronized void syncInit() {
if (instance == null) {
instance = new TimeCacheUtil();
}
}
public static TimeCacheUtil getInstance() {
if (instance == null) {
syncInit();
}
return instance;
}
/**
* 设置缓存值和时间
*
* @param key 键
* @param data 数据
* @param cachedMillis 缓存时间,单位:毫秒
* @return 数据
*/
@SuppressWarnings("unchecked")
public <T> T put(String key, T data, long cachedMillis) {
DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
if (dataWrapper != null) {
// 更新
dataWrapper.update(data, cachedMillis);
} else {
dataWrapper = new DataWrapper<T>(data, cachedMillis);
dataPool.put(key, dataWrapper);
initTimer(key, cachedMillis, null);
}
return data;
}
/**
* 设置缓存值并执行外部其他操作
*
* @param key 键
* @param data 数据
* @param cachedMillis 缓存时间,单位:毫秒
* @param cacheCleanService 超时清除服务,允许为空,这个类自己定义,传个实现类的对象进来即可
* @return 数据
*/
@SuppressWarnings("unchecked")
public <T> T put(String key, T data, long cachedMillis, CacheCleanService cacheCleanService) {
DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
if (dataWrapper != null) {
// 更新
dataWrapper.update(data, cachedMillis);
} else {
dataWrapper = new DataWrapper<T>(data, cachedMillis);
dataPool.put(key, dataWrapper);
initTimer(key, cachedMillis, cacheCleanService);
}
return data;
}
/**
* 定时器定时清理过期缓存
*
* @param key 键
* @param timeOut 超时时间,默认一天,单位:毫秒
* @param cacheCleanService 缓存清理服务
*/
private static void initTimer(String key, long timeOut, CacheCleanService cacheCleanService) {
long timerOutMillis = ObjectUtils.isEmpty(timeOut) ? DEFAULT_TIMER_MILLIS : timeOut;
executorService.schedule(new Runnable() {
@Override
public void run() {
if (dataPool.containsKey(key)) {
clearExpiredCaches(key);
if (null != cacheCleanService && StringUtils.isNotBlank(key)) {
// 自定义清除服务的方法
cacheCleanService.clean(key);
}
}
}
}, timerOutMillis, TimeUnit.MILLISECONDS);
}
@SuppressWarnings("unchecked")
public <T> T get(String key) {
DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
if (dataWrapper != null && !dataWrapper.isExpired()) {
return dataWrapper.data;
}
return null;
}
/**
* 清除缓存
*/
public void clear() {
dataPool.clear();
}
/**
* 删除指定key的value
*/
public void remove(String key) {
dataPool.remove(key);
}
/**
* 是否包含key
*
* @param key 键
* @return boolean
*/
public boolean containsKey(String key) {
return dataPool.containsKey(key);
}
/**
* 数据封装
*/
private class DataWrapper<T> {
/**
* 数据
*/
private T data;
/**
* 到期时间
*/
private long expiredTime;
/**
* 缓存时间
*/
private long cachedMillis;
private DataWrapper(T data, long cachedMillis) {
this.update(data, cachedMillis);
}
public void update(T data, long cachedMillis) {
this.data = data;
this.cachedMillis = cachedMillis;
this.updateExpiredTime();
}
public void updateExpiredTime() {
this.expiredTime = System.currentTimeMillis() + cachedMillis;
}
/**
* 数据是否过期
*
* @return
*/
public boolean isExpired() {
if (this.expiredTime > 0) {
return System.currentTimeMillis() > this.expiredTime;
}
return true;
}
}
/**
* 清除过期的缓存
*/
private static void clearExpiredCaches() {
List<String> expiredKeyList = new LinkedList<>();
for (Map.Entry<String, DataWrapper<?>> entry : dataPool.entrySet()) {
if (entry.getValue().isExpired()) {
expiredKeyList.add(entry.getKey());
}
}
for (String key : expiredKeyList) {
dataPool.remove(key);
}
}
/**
* 清除过期的缓存
*/
private static void clearExpiredCaches(String key) {
dataPool.remove(key);
}
}
自定义的接口CacheCleanService,按需定义即可
public interface CacheCleanService {
public void clean(String key);
}
实现类
@Service
public class CacheCleanServiceImpl implements CacheCleanService {
@Override
public void clean(String key) {
System.out.println("外部清理key: " + key + " 线程名称:" + Thread.currentThread().getName());
}
}
测试: