mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-04-05 17:37:53 +08:00
refactor: 重构 SaTokenDao 相关代码,减少重复代码
This commit is contained in:
parent
7c3febda60
commit
362b08b9b8
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
import java.util.List;
|
||||
@ -95,10 +94,7 @@ public interface SaTokenDao {
|
||||
* @param key 键名称
|
||||
* @return object
|
||||
*/
|
||||
default Object getObject(String key) {
|
||||
String jsonString = get(key);
|
||||
return SaManager.getSaJsonTemplate().jsonToObject(jsonString);
|
||||
}
|
||||
Object getObject(String key);
|
||||
|
||||
/**
|
||||
* 写入 Object,并设定存活时间 (单位: 秒)
|
||||
@ -107,46 +103,34 @@ public interface SaTokenDao {
|
||||
* @param object 值
|
||||
* @param timeout 存活时间(值大于0时限时存储,值=-1时永久存储,值=0或小于-2时不存储)
|
||||
*/
|
||||
default void setObject(String key, Object object, long timeout) {
|
||||
String jsonString = SaManager.getSaJsonTemplate().objectToJson(object);
|
||||
set(key, jsonString, timeout);
|
||||
}
|
||||
void setObject(String key, Object object, long timeout);
|
||||
|
||||
/**
|
||||
* 更新 Object (过期时间不变)
|
||||
* @param key 键名称
|
||||
* @param object 值
|
||||
*/
|
||||
default void updateObject(String key, Object object) {
|
||||
String jsonString = SaManager.getSaJsonTemplate().objectToJson(object);
|
||||
update(key, jsonString);
|
||||
}
|
||||
void updateObject(String key, Object object);
|
||||
|
||||
/**
|
||||
* 删除 Object
|
||||
* @param key 键名称
|
||||
*/
|
||||
default void deleteObject(String key) {
|
||||
delete(key);
|
||||
}
|
||||
void deleteObject(String key);
|
||||
|
||||
/**
|
||||
* 获取 Object 的剩余存活时间 (单位: 秒)
|
||||
* @param key 指定 key
|
||||
* @return 这个 key 的剩余存活时间
|
||||
*/
|
||||
default long getObjectTimeout(String key) {
|
||||
return getTimeout(key);
|
||||
}
|
||||
long getObjectTimeout(String key);
|
||||
|
||||
/**
|
||||
* 修改 Object 的剩余存活时间(单位: 秒)
|
||||
* @param key 指定 key
|
||||
* @param timeout 剩余存活时间
|
||||
*/
|
||||
default void updateObjectTimeout(String key, long timeout) {
|
||||
updateTimeout(key, timeout);
|
||||
}
|
||||
void updateObjectTimeout(String key, long timeout);
|
||||
|
||||
|
||||
// --------------------- SaSession 读写 (默认复用 Object 读写方法) ---------------------
|
||||
@ -156,52 +140,40 @@ public interface SaTokenDao {
|
||||
* @param sessionId sessionId
|
||||
* @return SaSession
|
||||
*/
|
||||
default SaSession getSession(String sessionId) {
|
||||
return (SaSession)getObject(sessionId);
|
||||
}
|
||||
SaSession getSession(String sessionId);
|
||||
|
||||
/**
|
||||
* 写入 SaSession,并设定存活时间(单位: 秒)
|
||||
* @param session 要保存的 SaSession 对象
|
||||
* @param timeout 过期时间(单位: 秒)
|
||||
*/
|
||||
default void setSession(SaSession session, long timeout) {
|
||||
setObject(session.getId(), session, timeout);
|
||||
}
|
||||
void setSession(SaSession session, long timeout);
|
||||
|
||||
/**
|
||||
* 更新 SaSession
|
||||
* @param session 要更新的 SaSession 对象
|
||||
*/
|
||||
default void updateSession(SaSession session) {
|
||||
updateObject(session.getId(), session);
|
||||
}
|
||||
void updateSession(SaSession session);
|
||||
|
||||
/**
|
||||
* 删除 SaSession
|
||||
* @param sessionId sessionId
|
||||
*/
|
||||
default void deleteSession(String sessionId) {
|
||||
deleteObject(sessionId);
|
||||
}
|
||||
void deleteSession(String sessionId);
|
||||
|
||||
/**
|
||||
* 获取 SaSession 剩余存活时间(单位: 秒)
|
||||
* @param sessionId 指定 SaSession
|
||||
* @return 这个 SaSession 的剩余存活时间
|
||||
*/
|
||||
default long getSessionTimeout(String sessionId) {
|
||||
return getObjectTimeout(sessionId);
|
||||
}
|
||||
long getSessionTimeout(String sessionId);
|
||||
|
||||
/**
|
||||
* 修改 SaSession 剩余存活时间(单位: 秒)
|
||||
* @param sessionId 指定 SaSession
|
||||
* @param timeout 剩余存活时间
|
||||
*/
|
||||
default void updateSessionTimeout(String sessionId, long timeout) {
|
||||
updateObjectTimeout(sessionId, timeout);
|
||||
}
|
||||
void updateSessionTimeout(String sessionId, long timeout);
|
||||
|
||||
|
||||
// --------------------- 会话管理 ---------------------
|
||||
|
@ -16,250 +16,71 @@
|
||||
package cn.dev33.satoken.dao;
|
||||
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.dao.auto.SaTokenDaoByStringFollowObject;
|
||||
import cn.dev33.satoken.dao.timedcache.SaTimedCache;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Sa-Token 持久层接口,默认实现类(基于内存 Map,系统重启后数据丢失)
|
||||
* Sa-Token 持久层接口,默认实现类,基于 SaTimedCache (内存 Map,系统重启后数据丢失)
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.10.0
|
||||
*/
|
||||
public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
|
||||
/**
|
||||
* 存储数据的集合
|
||||
*/
|
||||
public Map<String, Object> dataMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 存储数据过期时间的集合(单位: 毫秒), 记录所有 key 的到期时间 (注意存储的是到期时间,不是剩余存活时间)
|
||||
*/
|
||||
public Map<String, Long> expireMap = new ConcurrentHashMap<>();
|
||||
|
||||
// ------------------------ String 读写操作
|
||||
|
||||
@Override
|
||||
public String get(String key) {
|
||||
clearKeyByTimeout(key);
|
||||
return (String)dataMap.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, String value, long timeout) {
|
||||
if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
dataMap.put(key, value);
|
||||
expireMap.put(key, (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String key, String value) {
|
||||
if(getKeyTimeout(key) == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
dataMap.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeout(String key) {
|
||||
return getKeyTimeout(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
expireMap.put(key, (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000));
|
||||
}
|
||||
public class SaTokenDaoDefaultImpl implements SaTokenDaoByStringFollowObject {
|
||||
|
||||
public SaTimedCache timedCache = new SaTimedCache();
|
||||
|
||||
// ------------------------ Object 读写操作
|
||||
|
||||
@Override
|
||||
public Object getObject(String key) {
|
||||
clearKeyByTimeout(key);
|
||||
return dataMap.get(key);
|
||||
return timedCache.getObject(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(String key, Object object, long timeout) {
|
||||
if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
dataMap.put(key, object);
|
||||
expireMap.put(key, (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000));
|
||||
timedCache.setObject(key, object, timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObject(String key, Object object) {
|
||||
if(getKeyTimeout(key) == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
dataMap.put(key, object);
|
||||
timedCache.updateObject(key, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteObject(String key) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
timedCache.deleteObject(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getObjectTimeout(String key) {
|
||||
return getKeyTimeout(key);
|
||||
return timedCache.getObjectTimeout(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
expireMap.put(key, (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000));
|
||||
timedCache.updateObjectTimeout(key, timeout);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------ Session 读写操作
|
||||
// 使用接口默认实现
|
||||
|
||||
|
||||
// --------- 会话管理
|
||||
|
||||
@Override
|
||||
public List<String> searchData(String prefix, String keyword, int start, int size, boolean sortType) {
|
||||
return SaFoxUtil.searchList(expireMap.keySet(), prefix, keyword, start, size, sortType);
|
||||
return SaFoxUtil.searchList(timedCache.expireMap.keySet(), prefix, keyword, start, size, sortType);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------ 以下是一个定时缓存的简单实现,采用:惰性检查 + 异步循环扫描
|
||||
|
||||
// --------- 过期时间相关操作
|
||||
|
||||
/**
|
||||
* 如果指定的 key 已经过期,则立即清除它
|
||||
* @param key 指定 key
|
||||
*/
|
||||
void clearKeyByTimeout(String key) {
|
||||
Long expirationTime = expireMap.get(key);
|
||||
// 清除条件:
|
||||
// 1、数据存在。
|
||||
// 2、不是 [ 永不过期 ]。
|
||||
// 3、已经超过过期时间。
|
||||
if(expirationTime != null && expirationTime != SaTokenDao.NEVER_EXPIRE && expirationTime < System.currentTimeMillis()) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 key 的剩余存活时间 (单位:秒)
|
||||
* @param key 指定 key
|
||||
* @return 这个 key 的剩余存活时间
|
||||
*/
|
||||
long getKeyTimeout(String key) {
|
||||
// 由于数据过期检测属于惰性扫描,很可能此时这个 key 已经是过期状态了,所以这里需要先检查一下
|
||||
clearKeyByTimeout(key);
|
||||
|
||||
// 获取这个 key 的过期时间
|
||||
Long expire = expireMap.get(key);
|
||||
|
||||
// 如果 expire 数据不存在,说明框架没有存储这个 key,此时返回 NOT_VALUE_EXPIRE
|
||||
if(expire == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
|
||||
// 如果 expire 被标注为永不过期,则返回 NEVER_EXPIRE
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
return SaTokenDao.NEVER_EXPIRE;
|
||||
}
|
||||
|
||||
// ---- 代码至此,说明这个 key 是有过期时间的,且未过期,那么:
|
||||
|
||||
// 计算剩余时间并返回 (过期时间戳 - 当前时间戳) / 1000 转秒
|
||||
long timeout = (expire - System.currentTimeMillis()) / 1000;
|
||||
|
||||
// 小于零时,视为不存在
|
||||
if(timeout < 0) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
return timeout;
|
||||
}
|
||||
|
||||
// --------- 定时清理过期数据
|
||||
|
||||
/**
|
||||
* 执行数据清理的线程引用
|
||||
*/
|
||||
public Thread refreshThread;
|
||||
|
||||
/**
|
||||
* 是否继续执行数据清理的线程标记
|
||||
*/
|
||||
public volatile boolean refreshFlag;
|
||||
|
||||
/**
|
||||
* 清理所有已经过期的 key
|
||||
*/
|
||||
public void refreshDataMap() {
|
||||
for (String s : expireMap.keySet()) {
|
||||
clearKeyByTimeout(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化定时任务,定时清理过期数据
|
||||
*/
|
||||
public void initRefreshThread() {
|
||||
|
||||
// 如果开发者配置了 <=0 的值,则不启动定时清理
|
||||
if(SaManager.getConfig().getDataRefreshPeriod() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 启动定时刷新
|
||||
this.refreshFlag = true;
|
||||
this.refreshThread = new Thread(() -> {
|
||||
for (;;) {
|
||||
try {
|
||||
try {
|
||||
// 如果已经被标记为结束
|
||||
if( ! refreshFlag) {
|
||||
return;
|
||||
}
|
||||
// 执行清理
|
||||
refreshDataMap();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 休眠N秒
|
||||
int dataRefreshPeriod = SaManager.getConfig().getDataRefreshPeriod();
|
||||
if(dataRefreshPeriod <= 0) {
|
||||
dataRefreshPeriod = 1;
|
||||
}
|
||||
Thread.sleep(dataRefreshPeriod * 1000L);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
this.refreshThread.start();
|
||||
}
|
||||
|
||||
// --------- 组件生命周期
|
||||
|
||||
/**
|
||||
* 组件被安装时,开始刷新数据线程
|
||||
*/
|
||||
@Override
|
||||
public void init() {
|
||||
initRefreshThread();
|
||||
timedCache.initRefreshThread();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -267,6 +88,6 @@ public class SaTokenDaoDefaultImpl implements SaTokenDao {
|
||||
*/
|
||||
@Override
|
||||
public void destroy() {
|
||||
this.refreshFlag = false;
|
||||
timedCache.endRefreshThread();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.dao.auto;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
|
||||
/**
|
||||
* SaTokenDao 次级实现,以 JSON 序列化方式实现 Object 读写相关操作
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.41.0
|
||||
*/
|
||||
public interface SaTokenDaoByObjectFollowStringUseJsonSerializer extends SaTokenDaoBySessionFollowObject {
|
||||
|
||||
// --------------------- Object 读写 ---------------------
|
||||
|
||||
/**
|
||||
* 获取 Object,如无返空
|
||||
*
|
||||
* @param key 键名称
|
||||
* @return object
|
||||
*/
|
||||
@Override
|
||||
default Object getObject(String key) {
|
||||
String jsonString = get(key);
|
||||
return SaManager.getSaJsonTemplate().jsonToObject(jsonString);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入 Object,并设定存活时间 (单位: 秒)
|
||||
*
|
||||
* @param key 键名称
|
||||
* @param object 值
|
||||
* @param timeout 存活时间(值大于0时限时存储,值=-1时永久存储,值=0或小于-2时不存储)
|
||||
*/
|
||||
@Override
|
||||
default void setObject(String key, Object object, long timeout) {
|
||||
String jsonString = SaManager.getSaJsonTemplate().objectToJson(object);
|
||||
set(key, jsonString, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 Object (过期时间不变)
|
||||
* @param key 键名称
|
||||
* @param object 值
|
||||
*/
|
||||
@Override
|
||||
default void updateObject(String key, Object object) {
|
||||
String jsonString = SaManager.getSaJsonTemplate().objectToJson(object);
|
||||
update(key, jsonString);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 Object
|
||||
* @param key 键名称
|
||||
*/
|
||||
@Override
|
||||
default void deleteObject(String key) {
|
||||
delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Object 的剩余存活时间 (单位: 秒)
|
||||
* @param key 指定 key
|
||||
* @return 这个 key 的剩余存活时间
|
||||
*/
|
||||
@Override
|
||||
default long getObjectTimeout(String key) {
|
||||
return getTimeout(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改 Object 的剩余存活时间(单位: 秒)
|
||||
* @param key 指定 key
|
||||
* @param timeout 剩余存活时间
|
||||
*/
|
||||
@Override
|
||||
default void updateObjectTimeout(String key, long timeout) {
|
||||
updateTimeout(key, timeout);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.dao.auto;
|
||||
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.session.SaSession;
|
||||
|
||||
/**
|
||||
* SaTokenDao 次级实现:SaSession 读写跟随 Object 读写
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.41.0
|
||||
*/
|
||||
public interface SaTokenDaoBySessionFollowObject extends SaTokenDao {
|
||||
|
||||
// --------------------- SaSession 读写 (默认复用 Object 读写方法) ---------------------
|
||||
|
||||
/**
|
||||
* 获取 SaSession,如无返空
|
||||
* @param sessionId sessionId
|
||||
* @return SaSession
|
||||
*/
|
||||
default SaSession getSession(String sessionId) {
|
||||
return (SaSession)getObject(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入 SaSession,并设定存活时间(单位: 秒)
|
||||
* @param session 要保存的 SaSession 对象
|
||||
* @param timeout 过期时间(单位: 秒)
|
||||
*/
|
||||
default void setSession(SaSession session, long timeout) {
|
||||
setObject(session.getId(), session, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 SaSession
|
||||
* @param session 要更新的 SaSession 对象
|
||||
*/
|
||||
default void updateSession(SaSession session) {
|
||||
updateObject(session.getId(), session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 SaSession
|
||||
* @param sessionId sessionId
|
||||
*/
|
||||
default void deleteSession(String sessionId) {
|
||||
deleteObject(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 SaSession 剩余存活时间(单位: 秒)
|
||||
* @param sessionId 指定 SaSession
|
||||
* @return 这个 SaSession 的剩余存活时间
|
||||
*/
|
||||
default long getSessionTimeout(String sessionId) {
|
||||
return getObjectTimeout(sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改 SaSession 剩余存活时间(单位: 秒)
|
||||
* @param sessionId 指定 SaSession
|
||||
* @param timeout 剩余存活时间
|
||||
*/
|
||||
default void updateSessionTimeout(String sessionId, long timeout) {
|
||||
updateObjectTimeout(sessionId, timeout);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.dao.auto;
|
||||
|
||||
/**
|
||||
* SaTokenDao 次级实现:String 读写跟随 Object 读写
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.41.0
|
||||
*/
|
||||
public interface SaTokenDaoByStringFollowObject extends SaTokenDaoBySessionFollowObject {
|
||||
|
||||
// --------------------- String 读写 ---------------------
|
||||
|
||||
@Override
|
||||
default String get(String key) {
|
||||
return (String) getObject(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void set(String key, String value, long timeout) {
|
||||
setObject(key, value, timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void update(String key, String value) {
|
||||
updateObject(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void delete(String key) {
|
||||
deleteObject(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
default long getTimeout(String key) {
|
||||
return getObjectTimeout(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void updateTimeout(String key, long timeout) {
|
||||
updateObjectTimeout(key, timeout);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.dao.timedcache;
|
||||
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 一个定时缓存的简单实现,采用:惰性检查 + 异步循环扫描
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.41.0
|
||||
*/
|
||||
public class SaTimedCache {
|
||||
|
||||
/**
|
||||
* 存储数据的集合
|
||||
*/
|
||||
public Map<String, Object> dataMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 存储数据过期时间的集合(单位: 毫秒), 记录所有 key 的到期时间 (注意存储的是到期时间,不是剩余存活时间)
|
||||
*/
|
||||
public Map<String, Long> expireMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
// ------------------------ 基础 API 读写操作
|
||||
|
||||
public Object getObject(String key) {
|
||||
clearKeyByTimeout(key);
|
||||
return dataMap.get(key);
|
||||
}
|
||||
|
||||
public void setObject(String key, Object object, long timeout) {
|
||||
if(timeout == 0 || timeout <= SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
dataMap.put(key, object);
|
||||
expireMap.put(key, (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000));
|
||||
}
|
||||
|
||||
public void updateObject(String key, Object object) {
|
||||
if(getKeyTimeout(key) == SaTokenDao.NOT_VALUE_EXPIRE) {
|
||||
return;
|
||||
}
|
||||
dataMap.put(key, object);
|
||||
}
|
||||
|
||||
public void deleteObject(String key) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
}
|
||||
|
||||
public long getObjectTimeout(String key) {
|
||||
return getKeyTimeout(key);
|
||||
}
|
||||
|
||||
public void updateObjectTimeout(String key, long timeout) {
|
||||
expireMap.put(key, (timeout == SaTokenDao.NEVER_EXPIRE) ? (SaTokenDao.NEVER_EXPIRE) : (System.currentTimeMillis() + timeout * 1000));
|
||||
}
|
||||
|
||||
|
||||
// --------- 过期时间相关操作
|
||||
|
||||
/**
|
||||
* 如果指定的 key 已经过期,则立即清除它
|
||||
* @param key 指定 key
|
||||
*/
|
||||
void clearKeyByTimeout(String key) {
|
||||
Long expirationTime = expireMap.get(key);
|
||||
// 清除条件:
|
||||
// 1、数据存在。
|
||||
// 2、不是 [ 永不过期 ]。
|
||||
// 3、已经超过过期时间。
|
||||
if(expirationTime != null && expirationTime != SaTokenDao.NEVER_EXPIRE && expirationTime < System.currentTimeMillis()) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 key 的剩余存活时间 (单位:秒)
|
||||
* @param key 指定 key
|
||||
* @return 这个 key 的剩余存活时间
|
||||
*/
|
||||
long getKeyTimeout(String key) {
|
||||
// 由于数据过期检测属于惰性扫描,很可能此时这个 key 已经是过期状态了,所以这里需要先检查一下
|
||||
clearKeyByTimeout(key);
|
||||
|
||||
// 获取这个 key 的过期时间
|
||||
Long expire = expireMap.get(key);
|
||||
|
||||
// 如果 expire 数据不存在,说明框架没有存储这个 key,此时返回 NOT_VALUE_EXPIRE
|
||||
if(expire == null) {
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
|
||||
// 如果 expire 被标注为永不过期,则返回 NEVER_EXPIRE
|
||||
if(expire == SaTokenDao.NEVER_EXPIRE) {
|
||||
return SaTokenDao.NEVER_EXPIRE;
|
||||
}
|
||||
|
||||
// ---- 代码至此,说明这个 key 是有过期时间的,且未过期,那么:
|
||||
|
||||
// 计算剩余时间并返回 (过期时间戳 - 当前时间戳) / 1000 转秒
|
||||
long timeout = (expire - System.currentTimeMillis()) / 1000;
|
||||
|
||||
// 小于零时,视为不存在
|
||||
if(timeout < 0) {
|
||||
dataMap.remove(key);
|
||||
expireMap.remove(key);
|
||||
return SaTokenDao.NOT_VALUE_EXPIRE;
|
||||
}
|
||||
return timeout;
|
||||
}
|
||||
|
||||
// --------- 定时清理过期数据
|
||||
|
||||
/**
|
||||
* 执行数据清理的线程引用
|
||||
*/
|
||||
public Thread refreshThread;
|
||||
|
||||
/**
|
||||
* 是否继续执行数据清理的线程标记
|
||||
*/
|
||||
public volatile boolean refreshFlag;
|
||||
|
||||
/**
|
||||
* 清理所有已经过期的 key
|
||||
*/
|
||||
public void refreshDataMap() {
|
||||
for (String s : expireMap.keySet()) {
|
||||
clearKeyByTimeout(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化定时任务,定时清理过期数据
|
||||
*/
|
||||
public void initRefreshThread() {
|
||||
|
||||
// 如果开发者配置了 <=0 的值,则不启动定时清理
|
||||
if(SaManager.getConfig().getDataRefreshPeriod() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 启动定时刷新
|
||||
this.refreshFlag = true;
|
||||
this.refreshThread = new Thread(() -> {
|
||||
for (;;) {
|
||||
try {
|
||||
try {
|
||||
// 如果已经被标记为结束
|
||||
if( ! refreshFlag) {
|
||||
return;
|
||||
}
|
||||
// 执行清理
|
||||
refreshDataMap();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 休眠N秒
|
||||
int dataRefreshPeriod = SaManager.getConfig().getDataRefreshPeriod();
|
||||
if(dataRefreshPeriod <= 0) {
|
||||
dataRefreshPeriod = 1;
|
||||
}
|
||||
Thread.sleep(dataRefreshPeriod * 1000L);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
this.refreshThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束定时任务
|
||||
*/
|
||||
public void endRefreshThread() {
|
||||
this.refreshFlag = false;
|
||||
}
|
||||
|
||||
}
|
@ -37,6 +37,7 @@
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
<fastjson2.version>2.0.15</fastjson2.version>
|
||||
<redisson.version>3.19.0</redisson.version>
|
||||
<hutool-cache.version>5.8.36</hutool-cache.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@ -257,6 +258,13 @@
|
||||
<version>${jjwt.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Hutool Cache -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-cache</artifactId>
|
||||
<version>${hutool-cache.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token 自身依赖 -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
|
@ -24,12 +24,12 @@
|
||||
<module>sa-token-redis-jackson</module>
|
||||
<module>sa-token-fastjson</module>
|
||||
<module>sa-token-fastjson2</module>
|
||||
<module>sa-token-hutool-timed-cache</module>
|
||||
|
||||
<module>sa-token-redisson-jackson</module>
|
||||
<module>sa-token-redisson-jackson2</module>
|
||||
<module>sa-token-redisx</module>
|
||||
<module>sa-token-alone-redis</module>
|
||||
<module>sa-token-hutool-timed-cache</module>
|
||||
<module>sa-token-dialect-thymeleaf</module>
|
||||
<module>sa-token-freemarker</module>
|
||||
<module>sa-token-sso</module>
|
||||
|
@ -26,7 +26,7 @@ import cn.dev33.satoken.strategy.SaStrategy;
|
||||
* @author click33
|
||||
* @since 1.41.0
|
||||
*/
|
||||
public class SaTokenPluginFastjson implements SaTokenPlugin {
|
||||
public class SaTokenPluginForFastjson implements SaTokenPlugin {
|
||||
|
||||
@Override
|
||||
public void setup() {
|
@ -1 +1 @@
|
||||
cn.dev33.satoken.plugin.SaTokenPluginFastjson
|
||||
cn.dev33.satoken.plugin.SaTokenPluginForFastjson
|
@ -26,7 +26,7 @@ import cn.dev33.satoken.strategy.SaStrategy;
|
||||
* @author click33
|
||||
* @since 1.41.0
|
||||
*/
|
||||
public class SaTokenPluginFastjson2 implements SaTokenPlugin {
|
||||
public class SaTokenPluginForFastjson2 implements SaTokenPlugin {
|
||||
|
||||
@Override
|
||||
public void setup() {
|
@ -1 +1 @@
|
||||
cn.dev33.satoken.plugin.SaTokenPluginFastjson2
|
||||
cn.dev33.satoken.plugin.SaTokenPluginForFastjson2
|
@ -17,19 +17,14 @@
|
||||
<description>sa-token integrate hutool-TimedCache</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- sa-token-spring-boot-starter -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-core</artifactId>
|
||||
</dependency>
|
||||
<!-- Hutool Cache -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-cache</artifactId>
|
||||
<version>5.8.27</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
||||
|
@ -17,6 +17,7 @@ package cn.dev33.satoken.dao;
|
||||
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.dao.auto.SaTokenDaoByStringFollowObject;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import cn.hutool.cache.CacheUtil;
|
||||
import cn.hutool.cache.impl.CacheObj;
|
||||
@ -31,7 +32,7 @@ import java.util.List;
|
||||
* @author click33
|
||||
* @since 1.38.0
|
||||
*/
|
||||
public class SaTokenDaoForHutoolTimedCache implements SaTokenDao {
|
||||
public class SaTokenDaoForHutoolTimedCache implements SaTokenDaoByStringFollowObject {
|
||||
|
||||
//
|
||||
/**
|
||||
@ -41,39 +42,6 @@ public class SaTokenDaoForHutoolTimedCache implements SaTokenDao {
|
||||
public TimedCache<String, Object> timedCache = CacheUtil.newTimedCache(1000);
|
||||
|
||||
|
||||
// ------------------------ String 读写操作
|
||||
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return (String) getObject(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, String value, long timeout) {
|
||||
setObject(key, value, timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(String key, String value) {
|
||||
updateObject(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String key) {
|
||||
deleteObject(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeout(String key) {
|
||||
return getObjectTimeout(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTimeout(String key, long timeout) {
|
||||
updateObjectTimeout(key, timeout);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------ Object 读写操作
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2020-2099 sa-token.cc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package cn.dev33.satoken.plugin;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import cn.dev33.satoken.dao.SaTokenDaoForHutoolTimedCache;
|
||||
|
||||
/**
|
||||
* SaToken 插件安装:DAO 扩展 - Hutool-TimedCache 版
|
||||
*
|
||||
* @author click33
|
||||
* @since 1.41.0
|
||||
*/
|
||||
public class SaTokenPluginForHutoolCache implements SaTokenPlugin {
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
|
||||
SaManager.setSaTokenDao(new SaTokenDaoForHutoolTimedCache());
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1 @@
|
||||
cn.dev33.satoken.plugin.SaTokenPluginForHutoolCache
|
@ -1 +0,0 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.dao.SaTokenDaoForHutoolTimedCache
|
@ -1 +0,0 @@
|
||||
cn.dev33.satoken.dao.SaTokenDaoForHutoolTimedCache
|
@ -16,6 +16,7 @@
|
||||
package cn.dev33.satoken.dao.impl;
|
||||
|
||||
import cn.dev33.satoken.dao.SaTokenDao;
|
||||
import cn.dev33.satoken.dao.auto.SaTokenDaoByObjectFollowStringUseJsonSerializer;
|
||||
import cn.dev33.satoken.util.SaFoxUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
@ -34,7 +35,7 @@ import java.util.concurrent.TimeUnit;
|
||||
* @since 1.34.0
|
||||
*/
|
||||
@Component
|
||||
public class SaTokenDaoForRedisTemplate implements SaTokenDao {
|
||||
public class SaTokenDaoForRedisTemplate implements SaTokenDaoByObjectFollowStringUseJsonSerializer {
|
||||
|
||||
public StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
|
@ -15,12 +15,11 @@
|
||||
*/
|
||||
package cn.dev33.satoken.core.json;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import cn.dev33.satoken.exception.NotImplException;
|
||||
import cn.dev33.satoken.json.SaJsonTemplateDefaultImpl;
|
||||
import cn.dev33.satoken.util.SoMap;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* json默认实现类测试
|
||||
@ -35,11 +34,11 @@ public class SaJsonTemplateDefaultImplTest {
|
||||
SaJsonTemplateDefaultImpl saJsonTemplate = new SaJsonTemplateDefaultImpl();
|
||||
// 组件未实现
|
||||
Assertions.assertThrows(NotImplException.class, () -> {
|
||||
saJsonTemplate.parseJsonToMap("{}");
|
||||
saJsonTemplate.jsonToMap("{}");
|
||||
});
|
||||
// 组件未实现
|
||||
Assertions.assertThrows(NotImplException.class, () -> {
|
||||
saJsonTemplate.toJsonString(SoMap.getSoMap("name", "zhangsan"));
|
||||
saJsonTemplate.objectToJson(SoMap.getSoMap("name", "zhangsan"));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ public class SaAnnotationControllerTest {
|
||||
|
||||
// 转 Map
|
||||
String content = mvcResult.getResponse().getContentAsString();
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().parseJsonToMap(content);
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(content);
|
||||
|
||||
// 转 SaResult 对象
|
||||
return new SaResult().setMap(map);
|
||||
|
@ -96,7 +96,7 @@ public class MoreControllerTest {
|
||||
|
||||
// 转 Map
|
||||
String content = mvcResult.getResponse().getContentAsString();
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().parseJsonToMap(content);
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(content);
|
||||
// 转 SaResult 对象
|
||||
SaResult res = new SaResult().setMap(map);
|
||||
Assertions.assertEquals(res.getCode(), 903);
|
||||
@ -117,7 +117,7 @@ public class MoreControllerTest {
|
||||
|
||||
// 转 Map
|
||||
String content2 = mvcResult2.getResponse().getContentAsString();
|
||||
Map<String, Object> map2 = SaManager.getSaJsonTemplate().parseJsonToMap(content2);
|
||||
Map<String, Object> map2 = SaManager.getSaJsonTemplate().jsonToMap(content2);
|
||||
// 转 SaResult 对象
|
||||
SaResult res2 = new SaResult().setMap(map2);
|
||||
Assertions.assertEquals(res2.getCode(), 200);
|
||||
@ -139,7 +139,7 @@ public class MoreControllerTest {
|
||||
|
||||
// 转 Map
|
||||
String content = mvcResult.getResponse().getContentAsString();
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().parseJsonToMap(content);
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(content);
|
||||
|
||||
// 转 SaResult 对象
|
||||
return new SaResult().setMap(map);
|
||||
|
@ -202,7 +202,7 @@ public class RouterControllerTest {
|
||||
|
||||
// 转 Map
|
||||
String content = mvcResult.getResponse().getContentAsString();
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().parseJsonToMap(content);
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(content);
|
||||
|
||||
// 转 SaResult 对象
|
||||
SaResult res = new SaResult().setMap(map);
|
||||
@ -261,7 +261,7 @@ public class RouterControllerTest {
|
||||
|
||||
// 转 Map
|
||||
String content = mvcResult.getResponse().getContentAsString();
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().parseJsonToMap(content);
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(content);
|
||||
|
||||
// 转 SaResult 对象
|
||||
return new SaResult().setMap(map);
|
||||
|
@ -132,7 +132,7 @@ public class SaSameTokenControllerTest {
|
||||
|
||||
// 转 Map
|
||||
String content = mvcResult.getResponse().getContentAsString();
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().parseJsonToMap(content);
|
||||
Map<String, Object> map = SaManager.getSaJsonTemplate().jsonToMap(content);
|
||||
|
||||
// 转 SaResult 对象
|
||||
return new SaResult().setMap(map);
|
||||
|
@ -743,18 +743,18 @@ public class BasicsTest {
|
||||
|
||||
// map 转 json
|
||||
SoMap map = SoMap.getSoMap("name", "zhangsan");
|
||||
String jsonString = saJsonTemplate.toJsonString(map);
|
||||
String jsonString = saJsonTemplate.objectToJson(map);
|
||||
Assertions.assertEquals(jsonString, "{\"name\":\"zhangsan\"}");
|
||||
|
||||
// 抛异常
|
||||
Assertions.assertThrows(SaJsonConvertException.class, () -> saJsonTemplate.toJsonString(new Object()));
|
||||
Assertions.assertThrows(SaJsonConvertException.class, () -> saJsonTemplate.objectToJson(new Object()));
|
||||
|
||||
// json 转 map
|
||||
Map<String, Object> map2 = saJsonTemplate.parseJsonToMap("{\"name\":\"zhangsan\"}");
|
||||
Map<String, Object> map2 = saJsonTemplate.jsonToMap("{\"name\":\"zhangsan\"}");
|
||||
Assertions.assertEquals(map2.get("name"), "zhangsan");
|
||||
|
||||
// 抛异常
|
||||
Assertions.assertThrows(SaJsonConvertException.class, () -> saJsonTemplate.parseJsonToMap(""));
|
||||
Assertions.assertThrows(SaJsonConvertException.class, () -> saJsonTemplate.jsonToMap(""));
|
||||
}
|
||||
|
||||
// 测试过滤器、拦截器 基础API
|
||||
|
Loading…
Reference in New Issue
Block a user