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,为使用缓存,而是直接查数据库了。。。
一个简易的缓存就到这里了。。。。