通常,我们为了避免频繁的查询访问数据库或者第三方接口,会把查询结果缓存到redis或者memcached之类的nosql数据库中,避免数据库或者网络开销过大导致程序效率太低或者雪崩效应,但是代码中频繁的操作缓存,会让代码过于冗长,可以通过自定义注解的方式封装缓存的操作,使代码更简洁,话不多说,直接上代码:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface EnableCacheService {
/**
* key前缀
*/
String keyPrefix();
/**
* key主体,spel表示,例:#id(取形参中id的值)
*/
String fieldKey();
/**
* 过期时间
*/
int expireTime() default 3600;
TimeUnit timeUnit() default TimeUnit.SECONDS;
CacheOperation cacheOperation();
/**
* 缓存操作类型
*/
enum CacheOperation {
QUERY, // 查询
UPDATE, // 修改
DELETE; // 删除
}
}
/**
* EnableRedisService 注解切面处理
* @author: Iffie
* @date: 2018年9月29日
*/
@Aspect
@Component
@Slf4j
@SuppressWarnings("all")
public class CacheServiceAspect {
@Pointcut("@annotation(com.iffie.core.EnableCacheService)")
public void dealCacheServiceCut(){}
@Autowired
private RedisTemplate redisTemplate;
@Around(value = "dealCacheServiceCut()")
@SuppressWarnings("all")
public Object dealCacheService(ProceedingJoinPoint point) throws Throwable{
try {
Method method = getMethod(point);
// 获取注解对象
EnableCacheService cacheServiceAnnotation = method.getAnnotation(EnableCacheService.class);
//所有参数
Object[] args = point.getArgs();
String fieldKey = parseKey(cacheServiceAnnotation.fieldKey(), method, args);
if(StringUtils.isEmpty(fieldKey)) {
return point.proceed();
}
String cacheKey = cacheServiceAnnotation.keyPrefix()+fieldKey;
log.info("{} enable cache service,cacheKey:{}",point.getSignature(),cacheKey);
CacheOperation cacheOperation = cacheServiceAnnotation.cacheOperation();
if(cacheOperation==CacheOperation.QUERY) {
return processQuery(point, cacheServiceAnnotation, cacheKey);
}
if(cacheOperation==CacheOperation.UPDATE||cacheOperation==CacheOperation.DELETE) {
return processUpdateAndDelete(point, cacheKey);
}
} catch (Exception e) {
log.error("dealCacheService error,JoinPoint:{}",point.getSignature(),e);
}
return point.proceed();
}
/**
* 查询处理
*/
private Object processQuery(ProceedingJoinPoint point, EnableCacheService cacheServiceAnnotation, String cacheKey)
throws Throwable {
if(redisTemplate.hasKey(cacheKey)) {
log.info("{} enable cache service,has cacheKey:{} , return",point.getSignature(),cacheKey);
return redisTemplate.opsForValue().get(cacheKey);
}else {
Object result = null;
try {
return result = point.proceed();
} finally {
redisTemplate.opsForValue().
set(cacheKey, result, cacheServiceAnnotation.expireTime(), cacheServiceAnnotation.timeUnit());
log.info("after {} proceed,save result to cache,redisKey:{},save content:{}",point.getSignature(),cacheKey,result);
}
}
}
/**
*删除和修改处理
*/
private Object processUpdateAndDelete(ProceedingJoinPoint point, String cacheKey)
throws Throwable {
//通常来讲,数据库update操作后,只需删除掉原来在缓存中的数据,下次查询时就会刷新
try {
return point.proceed();
} finally {
redisTemplate.delete(cacheKey);
}
}
private Method getMethod(JoinPoint joinPoint) throws Exception {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
return method;
}
/**
* 获取redis的key
*/
private String parseKey(String fieldKey,Method method,Object [] args){
//获取被拦截方法参数名列表(使用Spring支持类库)
LocalVariableTableParameterNameDiscoverer u =
new LocalVariableTableParameterNameDiscoverer();
String [] paraNameArr=u.getParameterNames(method);
//使用SPEL进行key的解析
ExpressionParser parser = new SpelExpressionParser();
//SPEL上下文
StandardEvaluationContext context = new StandardEvaluationContext();
//把方法参数放入SPEL上下文中
for(int i=0;i<paraNameArr.length;i++){
context.setVariable(paraNameArr[i], args[i]);
}
return parser.parseExpression(fieldKey).getValue(context,String.class);
}
}
@EnableCacheService(keyPrefix=Constants.USER_REDIS_KEY_PREFIX,
fieldKey="#id",cacheOperation=CacheOperation.QUERY)
public User expressIssueInfo(String id) {
return userMapper.selectByPrimaryKey(id);
}