1. 项目背景
项目中有一些统计报表等等已经统计好的,不会变化的数据,需要展示给前端。这时,采用的方法是使用redis缓存json结果,加快查询速度。并且使用自定义注解作为切面,具体执行方法为切点,只要在具体需要缓存的Controller上面添加@CacheHandle注解即可。Redis里面存在数据的话去Redis缓存里面查找。否则去调用具体的执行方法到数据库查询。这种方法在于对代码的侵入性比较低,只用设计缓存的名称和失效策略,以及在需要缓存处理的方法上面加一个自定义的注解。
2. 源码介绍
2.1 自定义注解类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheHandle {
public ACTION action() default ACTION.ADD;
public SERVICE_NAME[] service_name() default {};
public static enum SERVICE_NAME {
OVERVIEW("overview"), INTERIOR("interior"), THIRD_PARTY("third_party");
private String value;
private SERVICE_NAME(String value) {
this.value = value;
}
public String value() {
return value;
}
}
public static enum ACTION {
ADD("add"), DELETE("delete");
private String value;
private ACTION(String value) {
this.value = value;
}
public String value() {
return value;
}
}
}
2.2 切面缓存处理类
/**
* 缓存处理
*
* @author licf
*/
@Component
@Aspect
public class CacheAspect implements Ordered {
private Logger log = Logger.getLogger(CacheAspect.class);
@Around("within(com.clickplus.controller..*) && @annotation(ccHandle))")
public Object handleReq(ProceedingJoinPoint jp, CacheHandle ccHandle) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
long tenant_id = ((TenantUser) request.getSession().getAttribute(Constants.USER_INFO)).getTenantID()
.longValue();
if (CacheHandle.ACTION.ADD.value().equals(ccHandle.action().value())) {// 增加缓存
Object[] objects = jp.getArgs();
JSONObject json = null;
for (int i = 0; i < objects.length; i++) {
if (objects[i] instanceof JSONObject)
json = (JSONObject) objects[i];
}
CacheHandle.SERVICE_NAME temp = null;
if (ccHandle.service_name().length == 1)
temp = ccHandle.service_name()[0];
if (temp != null) {
String key = "dataCache_" + tenant_id + "_" + temp.value() + "_" + request.getRequestURI() + "_"
+ EncryptionUtil.parseStrToMd5L32(JSON.toJSONString(json));
String start_time = json.getString("start_time");
String end_time = json.getString("end_time");
Set<String> dateSet = null;
try {
dateSet = DateUtil.getDateSet(start_time, end_time);
} catch (Exception e) {
if (Constants.DEBUG)
e.printStackTrace();
return jp.proceed();
}
String today = DateUtil.date2String(new Date(), DateUtil.DEFAULT_DATE);
if (dateSet.contains(today)) {
return jp.proceed();
} else {
Object res = null;
JedisPool pool = null;
Jedis jedis = null;
try {
pool = RedisUtil.getInstance().getJedisPool();
jedis = pool.getResource();
String result = jedis.get(key);
if (result == null) {
res = jp.proceed();
// 方便测试
String temp_cache = String.valueOf(res);
if (res instanceof String) {
JSONObject o = JSON.parseObject(String.valueOf(res));
o.put("isCache", true);
temp_cache = JSON.toJSONString(o);
}
jedis.setex(key, 24 * 60 * 60, temp_cache);// 24小时
return res;
} else {
return result;
}
} catch (Exception e) {
if (Constants.DEBUG)
log.debug("RedisUtil执行方法报错", e);
if (res != null)
return res;
else
return jp.proceed();
} finally {
if (pool != null && jedis != null)
pool.returnResource(jedis);
}
}
} else {
return jp.proceed();
}
} else if (CacheHandle.ACTION.DELETE.value().equals(ccHandle.action().value())) {
CacheHandle.SERVICE_NAME[] temp = ccHandle.service_name();
String[] keys = new String[temp.length];
for (int j = 0; j < keys.length; j++) {
keys[j] = "dataCache_" + tenant_id + "_" + temp[j].value() + "_*_*";
}
Object res = jp.proceed();
JSONObject json = JSON.parseObject(String.valueOf(res));
if (json.getBooleanValue("success")) {
JedisPool pool = null;
Jedis jedis = null;
try {
pool = RedisUtil.getInstance().getJedisPool();
jedis = pool.getResource();
List<String> result = new ArrayList<String>();
for (int i = 0; i < keys.length; i++) {
result.addAll(jedis.keys(keys[i]));
}
if (result.size() > 0) {
String[] cache_keys = new String[result.size()];
cache_keys = result.toArray(cache_keys);
jedis.del(cache_keys);
}
} catch (Exception e) {
if (Constants.DEBUG)
log.debug("RedisUtil执行方法报错", e);
} finally {
if (pool != null && jedis != null)
pool.returnResource(jedis);
}
}
return res;
} else {
return jp.proceed();
}
}
@Override
public int getOrder() {
return 3;
}
}
此处需要注意的是key的生成策略,一般以表名+登录用户ID + 接口名 +请求json的md5值,如下所示:
dataCache_3_interior_/ajax/interior_overview/get_terminal_overview_fa09c35f218fa7777cea8aac37b255ac
value值一般是json格式的返回结果。如果找到了直接返回给前端页面,没找到或者出错,则添加到缓存。具体一些key的生成策略可以根据实际选用。
还可以参考自定义注解与AOP,Redis的结合