1. 介紹
目前業界使用廣泛的內存數據存儲有redis、Memcached。相比 Memcached,Redis 支持更豐富的數據結構。同時支持數據持久化。除此之外,Redis 還提供一些類數據庫的特性,比如事務,HA,主從庫。可以說 Redis 兼具了緩存系統和數據庫的一些特性,因此有着豐富的應用場景。不過本篇文件的重點不是redis,重點是手寫一個簡易的內存型緩存數據庫。
2. 緩存的基本功能
作爲緩存,一些基本的功能:可以存儲數據、獲取數據、刪除數據、數據淘汰策略等等。。。
3. 編碼
瞭解了基本功能後,下面就開始編碼啦。。。
3.1 定義基本方法
緩存的頂級接口。。。
Cache.java (interface)
/**
*
* 緩存頂級接口
* @author reyco
*
*/
public interface Cache {
/**
* 添加緩存對象
* @param key 緩存的key,通過該key可以獲取對應的緩存對象
* @param obj 緩存的對象...
* @return
*/
public Boolean put(String key, Object obj);
/**
* 添加緩存對象
* @param key 緩存的key,通過該key可以獲取對應的緩存對象
* @param obj 緩存的對象...
* @param duration 存活時長...
* @return
*/
public Boolean put(String key, Object obj,Long duration);
/**
* 獲取緩存對象
* @param key 緩存的key,通過該key可以獲取對應的緩存對象
* @return
*/
public Object get(String key);
/**
* 緩存的數量大小
* @return
*/
public Integer getSize();
/**
* 移除緩存
* @param key 緩存的key,通過該key可以獲取對應的緩存對象
* @return
*/
public Boolean remove(String key);
/**
* 清空緩存
* @return
*/
public Boolean clear();
/**
* 緩存淘汰策略
*/
public void removeStrategy();
}
3.2 緩存對象數據結構基本信息
ExpireInfo.java
/**
* 描述緩存對象
* @author reyco
*
*/
public class ExpireInfo {
/**
* 開始時間
*/
private Date startTime;
/**
* 存活時長
*/
private Long duration;
/**
* 結束時間毫秒數
*/
private Long ExpireTime;
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Long getDuration() {
return duration;
}
public void setDuration(Long duration) {
this.duration = duration;
}
public Long getExpireTime() {
return ExpireTime;
}
public void setExpireTime(Long expireTime) {
ExpireTime = expireTime;
}
}
3.3 基本方法的實現
ConcurrentHashMapCache.java 實現
/**
* 通過靜態內部類實現単例模式
* 這種方法:線程安全,調用效率高,並且實現了延時加載
* @author reyco
*
*/
public class ConcurrentHashMapCache implements Cache {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 緩存對象 key value
*/
private final static ConcurrentMap<String, Object> store = new ConcurrentHashMap<>();
/**
* 緩存的信息: 過去時間。。。
*/
private final static ConcurrentMap<String, ExpireInfo> storeObj = new ConcurrentHashMap<>();
/**
* 私有構造器
*/
private ConcurrentHashMapCache() {}
/**
* 私有獲取屬性的靜態內部類
* @author reyco
*
*/
private static class Instance {
private static final ConcurrentHashMapCache instance = new ConcurrentHashMapCache();
}
/**
* 方法沒有同步,效率高!
*
* @return
*/
public static ConcurrentHashMapCache getInstance() {
return Instance.instance;
}
/**
* 添加元素
*
* @param key
* @param value
* @return
*/
@Override
public Boolean put(String key, Object value) {
return put(key, value, null);
}
/**
* 添加元素
* @param key
* key
* @param value
* value
* @param duration
* 有效時間/單位秒
* @return
*/
@Override
public Boolean put(String key, Object obj, Long duration) {
if (StringUtils.isBlank(key) || null == obj) {
return false;
}
Long ExpireTime = 0L;
// 默認緩存不失效時長
long currentTimeMillis = System.currentTimeMillis();
if (null == duration || duration == -1) {
// 默認緩存不失效
ExpireTime = Long.MAX_VALUE;
}else {
ExpireTime = currentTimeMillis + 1000*duration;
}
// 構建緩存描述對象
ExpireInfo expireInfo = new ExpireInfo();
expireInfo.setStartTime(new Date());
expireInfo.setDuration(duration);
expireInfo.setExpireTime(ExpireTime);
//添加緩存
store.put(key, obj);
storeObj.put(key, expireInfo);
logger.debug("key="+key+",value="+obj+",expireTime="+ExpireTime+",添加到緩存中.");
return true;
}
/**
* 獲取元素
* @param key
* @return
*/
@Override
public Object get(String key) {
if (StringUtils.isBlank(key)) {
throw new RuntimeException("key Can not be null");
}
// 判斷key是否存在
if (!storeObj.containsKey(key)) {
return null;
}
// 是否過期
ExpireInfo expireInfo = storeObj.get(key);
long nowTime = System.currentTimeMillis();
Long expireTime = expireInfo.getExpireTime();
if (nowTime > expireTime) {
// 過期
remove(key);
return null;
}
Object object = store.get(key);
logger.debug("獲取緩存,key="+key+",value="+object);
return object;
}
/**
* 獲取元素數量
* @param key
* @return
*/
@Override
public Integer getSize() {
return store.size();
}
/**
* 移除元素
* @param key
* @return
*/
@Override
public Boolean remove(String key) {
if (StringUtils.isBlank(key)) {
return false;
}
Object value = null;
if(store.containsKey(key)) {
value = store.get(key);
store.remove(key);
storeObj.remove(key);
}
logger.debug("key="+key+",value="+value+",已過期,被移除了.");
return true;
}
/**
* 清空元素
* @return
*/
@Override
public Boolean clear() {
store.clear();
storeObj.clear();
logger.debug("緩存被清空.");
return true;
}
/**
* 緩存刪除策略
*/
@Override
public void removeStrategy() {
for (Entry<String, ExpireInfo> entry : storeObj.entrySet()) {
ExpireInfo expireInfo = entry.getValue();
// 是否過期
long nowTime = System.currentTimeMillis();
Long expireTime = expireInfo.getExpireTime();
if (nowTime > expireTime) {
String key = entry.getKey();
Object object = store.get(key);
// 過期
remove(key);
}
}
}
}
3.4 定時刪除過期的緩存數據
使用springboot的定時任務比較方法。。。
@Component
public class CacheTask {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Scheduled(cron = "0 * * * * ?")
public void clear() {
logger.debug("緩存定時任務執行開始:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
ConcurrentHashMapCache instance = ConcurrentHashMapCache.getInstance();
instance.removeStrategy();
logger.debug("緩存定時任務執行結束:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
使用原生quartz:
@Component
public class TaskRemoveStrategy implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
ConcurrentHashMapCache instance = ConcurrentHashMapCache.getInstance();
instance.removeStrategy();
}
@PostConstruct
public void task() {
try {
Date startTime = new Date();
startTime.setTime(startTime.getTime()+1000*30);
// 創建一個JobDetail
JobDetail taskJobDetail = JobBuilder.newJob(TaskRemoveStrategy.class)
.withIdentity("TaskDetailJob","TaskDetailGroup")
.build();
// Cron表達式: 秒 分 時 日 月 周 年
CronTrigger taskTrigger = (CronTrigger)TriggerBuilder.newTrigger()
// 30秒後執行任務
.startAt(startTime)
// 每分鐘執行
.withSchedule(CronScheduleBuilder.cronSchedule("0 * * * * ? *"))
.build();
// 創建SchedulerFactory
SchedulerFactory sf = new StdSchedulerFactory();
// 創建Scheduler
Scheduler scheduler = sf.getScheduler();
scheduler.start();
scheduler.scheduleJob(taskJobDetail,taskTrigger);
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
3.4 操作緩存的工具類
public class CacheUtils {
private static ConcurrentHashMapCache getConcurrentHashMapCache() {
return ConcurrentHashMapCache.getInstance();
}
/**
* 添加元素
* @param key
* key
* @param value
* value
* @param duration
* 有效時間/單位秒
* @return
*/
public static void put(String key,Object value,Long expireTime) {
ConcurrentHashMapCache concurrentHashMapCache = getConcurrentHashMapCache();
concurrentHashMapCache.put(key,value,expireTime);
}
/**
* 添加元素
* @param key
* @param value
* @return
*/
public static void put(String key,Object value) {
ConcurrentHashMapCache concurrentHashMapCache = getConcurrentHashMapCache();
concurrentHashMapCache.put(key, value);
}
/**
* 獲取緩存對象
* @param key
* @return
*/
public static Object get(String key) {
ConcurrentHashMapCache concurrentHashMapCache = getConcurrentHashMapCache();
return concurrentHashMapCache.get(key);
}
/**
* 移除緩存對象
* @param key
* @return
*/
public static Object remove(String key) {
ConcurrentHashMapCache concurrentHashMapCache = getConcurrentHashMapCache();
return concurrentHashMapCache.remove(key);
}
}
3.5 測試——模擬session功能
LoginController.java
@RequestMapping("/api")
@RestController
public class LoginController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@RequestMapping("captcha")
public Result getCaptcha() {
Result result = new Result();
String code = CaptchaUtils.createCode();
String key = UUID.randomUUID().toString().replace("-", "");
CacheUtils.put(key, code,60L);
logger.info("生成驗證碼");
Captcha captcha = new Captcha(key,code);
result.setData(captcha);
result.setFlag(true);
return result;
}
@RequestMapping("/login")
public Result Login(String name,String password,String captcha,HttpServletRequest request,HttpServletResponse response) {
Result result = new Result();
if(StringUtils.isBlank(name) || StringUtils.isBlank(password)|| StringUtils.isBlank(captcha) ) {
result.setData(null);
result.setFlag(false);
result.setMsg("參數錯誤");
logger.info("參數錯誤");
return result;
}
if(!name.equals("admin") || !password.equals("123456")) {
result.setData(null);
result.setFlag(false);
result.setMsg("用戶名或密碼錯誤。。。");
logger.info("用戶名或密碼錯誤。。。");
return result;
}
String captchaCookie = CookieUtil.getCookieByName(request, "captcha");
if(StringUtils.isBlank(captchaCookie)) {
result.setData(null);
result.setFlag(false);
result.setMsg("沒有驗證碼");
logger.info("沒有驗證碼");
return result;
}
Object selCaptcha = CacheUtils.get(captchaCookie);
CacheUtils.remove(captchaCookie);
if(null == selCaptcha ) {
result.setData(null);
result.setFlag(false);
result.setMsg("驗證碼失效");
logger.info("驗證碼失效");
return result;
}
if(!captcha.equalsIgnoreCase(selCaptcha.toString())) {
result.setData(null);
result.setFlag(false);
result.setMsg("驗證碼錯誤");
logger.info("驗證碼錯誤");
return result;
}
String key = UUID.randomUUID().toString().replace("-", "");
UserEntity userEntity = new UserEntity();
userEntity.setId(1);
userEntity.setName(name);
userEntity.setPassword(key);
// 創建 token
CacheUtils.put(key, userEntity);
userEntity.setId(1);
result.setData(userEntity);
result.setFlag(true);
return result;
}
@RequestMapping("/checkUser")
public Result checkLogin(HttpServletRequest request) {
Result result = new Result();
String cookie = CookieUtil.getCookieByName(request, "kn_token");
if(null== cookie) {
result.setData(null);
result.setFlag(false);
result.setMsg("你未登錄");
return result;
}
Object object = CacheUtils.get(cookie);
if(null== object) {
result.setData(null);
result.setFlag(false);
result.setMsg("沒有session");
return result;
}
result.setData(object);
result.setFlag(true);
return result;
}
}
login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/jquery-3.2.1.js"></script>
<script type="text/javascript">
$(function(){
captcha();
})
function captcha() {
$.ajax({
type:'post',
dataType: "json",
url:"api/captcha",
data:{ time:new Date()},
success:function(data){
var flag = data.flag;
if(flag){
var value = data.data.key;
var name = "captcha";
var day = 0;
if(day !== 0){ //當設置的時間等於0時,不設置expires屬性,cookie在瀏覽器關閉後刪除
var expires = day * 24 * 60 * 60 * 1000;
var date = new Date(+new Date()+expires);
document.cookie = name + "=" + escape(value) + ";expires=" + date.toUTCString();
}else{
document.cookie = name + "=" + escape(value);
}
$("#captcha").text(data.data.value);
}else{
$(location).attr('href', 'login.html');
}
},
error:function(){
}
});
}
function login() {
var userEntity={"id":"1","name":$("#username").val(),"password":$("#password").val(),"captcha":$("#captchas").val()};//json對象
var userEntityJson = JSON.stringify(userEntity);
$.ajax({
type:'post',
dataType: "json",
url:"api/login",
data:userEntity,
beforeSend:function(data){
},
success:function(data){
var flag = data.flag;
console.log("當前登錄用戶是====="+data.flag);
if(flag){
var value = data.data.password;
var name = "kn_token";
var day = 0;
if(day !== 0){ //當設置的時間等於0時,不設置expires屬性,cookie在瀏覽器關閉後刪除
var expires = day * 24 * 60 * 60 * 1000;
var date = new Date(+new Date()+expires);
document.cookie = name + "=" + escape(value) + ";expires=" + date.toUTCString();
}else{
document.cookie = name + "=" + escape(value);
}
$(location).attr('href', 'user_manager.html');
}else{
$("#msg").html("<font color='red'>"+data.msg+"</font>");
}
},
complete:function(data){
},
error:function(){
$(location).attr('href', 'login.html');
}
})
}
</script>
</head>
<body>
<span id="msg"></span><br>
用戶名<input id="username" type="text" name="name"/><br>
密碼<input id="password" type="text" name="password"/><br>
驗證碼<input id="captchas" type="text" name="captcha"/><span id="captcha" onclick="captcha()"></span><br>
<input type="button" onclick="login()" value="login"/>
</body>
</html>
user_manager.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/jquery-3.2.1.js"></script>
<script type="text/javascript">
$(function(){
if(window !=top){
top.location.href=location.href;
}
$.ajax({
type:'post',
dataType: "json",
url:"api/checkUser",
data:{ time:new Date()},
success:function(data){
var flag = data.flag;
if(flag){
}else{
$(location).attr('href', 'login.html');
}
},
error:function(){
}
});
})
</script>
</head>
<body>
user_manager.html
</body>
</html>
效果圖:
|
|
|
4. 擴展——模擬springboot緩
4.1 定義註解
springboot緩存有@Cacheable、@CacheEvict...等註解;相應的創建我們自定義註解@MyCacheable、 @MyCacheEvict。。。
MyCacheable.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCacheable {
/**
* 緩存名稱
* @return
*/
String name() default "";
/**
* 緩存的keyGenerator
* @return
*/
String keyGenerator() default "";
/**
* 緩存的過期時間,默認30分鐘
* @return
*/
long expireTime() default 60*30L;
}
MyCacheEvict.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCacheEvict {
/**
* 緩存名稱
* @return
*/
String name() default "";
/**
* 緩存的keyGenerator
* @return
*/
String keyGenerator() default "";
}
4.1 定義Aop
CachePutAop.java
@SuppressWarnings("all")
@Component
@Aspect
public class CachePutAop {
@Pointcut("@annotation(com.reyco.kn.core.annotation.MyCacheable)")
public void cachePointcutPut(){
}
/**
* 如果緩存存在直接獲取緩存信息,否則獲取執行方法後,將結果添加到緩存中
* @param point
* @throws Throwable
*/
@Around("cachePointcutPut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
// 1.獲取相關數據
MethodSignature methodSignature = (MethodSignature)point.getSignature();
// 目標對象
Class<? extends MethodSignature> clazz = methodSignature.getClass();
// 目標方法
Method method = methodSignature.getMethod();
// 目標參數名
String[] paramNames = methodSignature.getParameterNames();
// 目標參數值
Object[] paramValues = point.getArgs();
// 獲取註解
MyCacheable myCacheable = method.getAnnotation(MyCacheable.class);
// key
String key = "";
// 3.是否有cacheablePut註解,如果緩存存在直接獲取緩存信息,否則獲取執行方法後,將結果添加到緩存中
if(null != myCacheable) {
String cacheName = getCacheName(myCacheable);
KeyGenerator keyGenerator = getKeyGenerator(myCacheable,clazz,method,paramNames,paramValues);
// 3.1有註解
key = getKey(cacheName,keyGenerator);
// 通過key獲取緩存value
Object object = CacheUtils.get(key);
if(null == object) {
// 緩存中不存在當前緩存,執行目標方法
object = point.proceed();
long expireTime = myCacheable.expireTime();
if(expireTime < 1) {
CacheUtils.put(key, object);
}else {
CacheUtils.put(key, object,expireTime);
}
}
return object;
}
return point.proceed();
}
/**
* 獲取key
* @param cacheName 緩存名稱
* @param keyGeneratorObj keyGeneratorObj對象
* @return
*/
private String getKey(String cacheName,KeyGenerator keyGeneratorObj) {
// String key = cacheName + keyGenerator;
String key = "";
// 緩存
String keyGenerator = keyGeneratorObj.getKeyGenerator();
key = cacheName + keyGenerator;
return key;
}
/**
* 獲取緩存名稱
* @param myCacheable
* @return
*/
private String getCacheName(MyCacheable myCacheable) {
// 緩存名稱
String cacheName = "";
String name = myCacheable.name();
if(!StringUtils.isBlank(name)) {
cacheName = name+"::";
}
return cacheName;
}
/**
* 獲取KeyGenerator
* @param myCacheEvict
* @param clazz
* @param method
* @param paramNames
* @param paramValues
* @return
*/
private KeyGenerator getKeyGenerator(MyCacheable myCacheable,Class<? extends MethodSignature> clazz,Method method, String[] paramNames, Object[] paramValues) {
// KeyGenerator
KeyGenerator keyGeneratorObj = null;
// 2.1有註解
String keyGenerator = myCacheable.keyGenerator();
if(StringUtils.isBlank(keyGenerator)) {
keyGenerator = createKeyGenerator(clazz,method,paramValues);
}else {
// 3.2key是否包含#字符
if (keyGenerator.contains("#")) {
// 3.3 獲取KeyGenerator的名稱
String keyGeneratorName = keyGenerator.substring(1);
// 3.4 獲取KeyGenerator值
for (Object paramValue : paramValues) {
for (int j = 0; j < paramNames.length; j++) {
if (keyGeneratorName.equals(paramNames[j])) {
// 如果註解的keyGenerator和參數的名稱一直,取對應參數的值作爲keyGenerator
keyGenerator = paramValues[j].toString();
}
}
}
}
}
keyGeneratorObj = new KeyGenerator();
keyGeneratorObj.setKeyGenerator(keyGenerator);
return keyGeneratorObj;
}
/**
* key的默認生成器
* @param target 目標對象
* @param method 目標方法
* @param params 目標方法參數
* @return
*/
private String createKeyGenerator(Object target, Method method, Object... params) {
// 緩存的key
StringBuilder sb = new StringBuilder();
sb.append(target.getClass());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
}
CacheDeleteAop.java
@SuppressWarnings("all")
@Component
@Aspect
public class CacheDeleteAop {
@Pointcut("@annotation(com.reyco.kn.core.annotation.MyCacheEvict)")
public void cachePointcutDelete() {
}
/**
* 如果緩存存在直接獲取緩存信息,否則獲取執行方法後,將結果添加到緩存中
*
* @param point
* @throws Throwable
*/
@Around("cachePointcutDelete()")
public void around(ProceedingJoinPoint point) throws Throwable {
// 1.獲取相關數據
MethodSignature methodSignature = (MethodSignature) point.getSignature();
// 目標對象
Class<? extends MethodSignature> clazz = methodSignature.getClass();
// 目標方法
Method method = methodSignature.getMethod();
// 獲取註解
MyCacheEvict myCacheEvict = method.getAnnotation(MyCacheEvict.class);
// key
String key = "";
// 2 是否有cacheableDelete註解,如果有走刪除緩存,否則向下執行
if (null != myCacheEvict) {
// 目標參數名
String[] paramNames = methodSignature.getParameterNames();
// 目標參數值
Object[] paramValues = point.getArgs();
// 緩存名稱
String cacheName = getCacheName(myCacheEvict);
// 2.1 獲取key
KeyGenerator keyGenerator = getKeyGenerator(myCacheEvict,clazz,method,paramNames,paramValues);
key = getKey(cacheName,keyGenerator);
// 2.2移除key的緩存
CacheUtils.remove(key);
}
// 3 執行目標方法
point.proceed();
}
/**
* 獲取key
* @param cacheName 緩存名稱
* @param keyGeneratorObj keyGeneratorObj對象
* @return
*/
private String getKey(String cacheName,KeyGenerator keyGeneratorObj) {
// String key = cacheName + keyGenerator;
String key = "";
// 緩存
String keyGenerator = keyGeneratorObj.getKeyGenerator();
key = cacheName + keyGenerator;
return key;
}
/**
* 獲取KeyGenerator
* @param myCacheEvict
* @param clazz
* @param method
* @param paramNames
* @param paramValues
* @return
*/
private KeyGenerator getKeyGenerator(MyCacheEvict myCacheEvict,Class<? extends MethodSignature> clazz,Method method, String[] paramNames, Object[] paramValues) {
// KeyGenerator
KeyGenerator keyGeneratorObj = null;
// 2.1有註解
String keyGenerator = myCacheEvict.keyGenerator();
if(StringUtils.isBlank(keyGenerator)) {
keyGenerator = createKeyGenerator(clazz,method,paramValues);
}else {
// 3.2key是否包含#字符
if (keyGenerator.contains("#")) {
// 3.3 獲取KeyGenerator的名稱
String keyGeneratorName = keyGenerator.substring(1);
// 3.4 獲取KeyGenerator值
for (Object paramValue : paramValues) {
for (int j = 0; j < paramNames.length; j++) {
if (keyGeneratorName.equals(paramNames[j])) {
// 如果註解的keyGenerator和參數的名稱一直,取對應參數的值作爲keyGenerator
keyGenerator = paramValues[j].toString();
}
}
}
}
}
keyGeneratorObj = new KeyGenerator();
keyGeneratorObj.setKeyGenerator(keyGenerator);
return keyGeneratorObj;
}
/**
* 獲取緩存名稱
* @param myCacheEvict
* @return
*/
private String getCacheName(MyCacheEvict myCacheEvict) {
// 緩存名稱
String cacheName = "";
String name = myCacheEvict.name();
if(!StringUtils.isBlank(name)) {
cacheName = name+"::";
}
return cacheName;
}
/**
* key的默認生成器
*
* @param target
* 目標對象
* @param method
* 目標方法
* @param params
* 目標方法參數
* @return
*/
private String createKeyGenerator(Object target, Method method, Object... params) {
// 緩存的KeyGenerator
StringBuilder sb = new StringBuilder();
sb.append(target.getClass());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
}
4.3 測試
4.3.1 編寫service
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@MyCacheable(key="#id",name="user",expireTime=20)
public UserEntity get(Integer id) {
return userDao.get(id);
}
/**
* 刪除
*/
@Override
@Transactional(propagation=Propagation.REQUIRED)
@MyCacheEvict(key="#id",name="user")
public void delete(Integer id) {
UserEntity userEntity = new UserEntity();
userEntity.setId(id);
userEntity.setState(1);
userDao.update(userEntity);
}
}
4.3.2 編寫測試
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestApplication {
@Autowired
UserService userService;
@Test
public void getGet() {
userService.get(1);
userService.get(1);
}
@Test
public void testDelete() {
userService.delete(1);
userService.get(1);
}
}
4.3.3 效果
2019-10-30 17:13:00.193 DEBUG 11464 --- [nio-8081-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public com.reyco.kn.core.domain.Result com.reyco.kn.core.controller.UserController.get(java.lang.Integer)
2019-10-30 17:13:00.239 INFO 11464 --- [nio-8081-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-10-30 17:13:00.243 WARN 11464 --- [nio-8081-exec-1] com.zaxxer.hikari.util.DriverDataSource : Registered driver with driverClassName=com.mysql.jdbc.Driver was not found, trying direct instantiation.
2019-10-30 17:13:00.900 INFO 11464 --- [nio-8081-exec-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2019-10-30 17:13:00.905 DEBUG 11464 --- [nio-8081-exec-1] com.reyco.kn.core.dao.UserDao.get : ==> Preparing: select * from `user` where state=0 and `id`=?
2019-10-30 17:13:00.925 DEBUG 11464 --- [nio-8081-exec-1] com.reyco.kn.core.dao.UserDao.get : ==> Parameters: 1(Integer)
2019-10-30 17:13:00.976 DEBUG 11464 --- [nio-8081-exec-1] com.reyco.kn.core.dao.UserDao.get : <== Total: 1
2019-10-30 17:13:00.979 DEBUG 11464 --- [nio-8081-exec-1] c.r.k.core.cache.ConcurrentHashMapCache : key=1,value=UserEntity [id=1,name=admin,password=123456,desc=null,state=0,gmtCreate=null,gmtModified=null],expireTime=1572428580978,添加到緩存中.
2019-10-30 17:13:00.979 DEBUG 11464 --- [nio-8081-exec-1] c.r.k.core.cache.ConcurrentHashMapCache : 獲取緩存,key=1,value=UserEntity [id=1,name=admin,password=123456,desc=null,state=0,gmtCreate=null,gmtModified=null]
2019-10-30 17:13:01.016 DEBUG 11464 --- [nio-8081-exec-1] m.m.a.RequestResponseBodyMethodProcessor : Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/json, application/*+json]
2019-10-30 17:13:01.017 DEBUG 11464 --- [nio-8081-exec-1] m.m.a.RequestResponseBodyMethodProcessor : Writing [com.reyco.kn.core.domain.Result@5ba33db]
2019-10-30 17:13:01.033 DEBUG 11464 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet : Completed 200 OK
第一次get去查數據庫啦,第二次就使用了緩存。。。
2019-10-30 17:17:22.046 DEBUG 11464 --- [nio-8081-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to public java.lang.String com.reyco.kn.core.controller.UserController.delete(java.lang.Integer)
2019-10-30 17:17:22.110 DEBUG 11464 --- [nio-8081-exec-3] c.r.k.core.cache.ConcurrentHashMapCache : key=1,value=UserEntity [id=1,name=admin,password=123456,desc=null,state=0,gmtCreate=null,gmtModified=null],已過期,被移除了.
2019-10-30 17:17:22.197 DEBUG 11464 --- [nio-8081-exec-3] com.reyco.kn.core.dao.UserDao.get : ==> Preparing: select * from `user` where state=0 and `id`=?
2019-10-30 17:17:22.197 DEBUG 11464 --- [nio-8081-exec-3] com.reyco.kn.core.dao.UserDao.get : ==> Parameters: 1(Integer)
2019-10-30 17:17:22.229 DEBUG 11464 --- [nio-8081-exec-3] com.reyco.kn.core.dao.UserDao.get : <== Total: 1
2019-10-30 17:17:22.230 DEBUG 11464 --- [nio-8081-exec-3] c.r.k.core.cache.ConcurrentHashMapCache : key=1,value=UserEntity [id=1,name=admin,password=123456,desc=null,state=0,gmtCreate=null,gmtModified=null],expireTime=1572428842230,添加到緩存中.
2019-10-30 17:17:22.230 DEBUG 11464 --- [nio-8081-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Using 'text/plain', given [*/*] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2019-10-30 17:17:22.231 DEBUG 11464 --- [nio-8081-exec-3] m.m.a.RequestResponseBodyMethodProcessor : Writing ["刪除成功"]
2019-10-30 17:17:22.233 DEBUG 11464 --- [nio-8081-exec-3] o.s.web.servlet.DispatcherServlet : Completed 200 OK
先delete掉緩存後,再去get,爲使用緩存,而是直接查數據庫了。。。
一個簡易的緩存就到這裏了。。。。