AOP+自定义注解完成Redis缓存操作 #
1 业务代码 #
以某查询为例,加缓存前的业务代码如下:
@Service
public class XxxServiceImpl implements XxxService {
@Autowired
private XxxMapper xxxMapper;
@Override
public Page page(Integer pageNum, Integer pageSize) {
return xxxMapper.page(pageNum, pageSize);
}
}
2 在业务代码中加入缓存操作 #
@Service
public class XxxServiceImpl implements XxxService {
@Autowired
private XxxMapper xxxMapper;
@Autowired
private Jedis jedis;
static final String PAGE_KEY = "page";
@Override
public Page page(Integer pageNum, Integer pageSize) {
// 1.拼接key并查询Redis中是否有缓存
String key = PAGE_KEY + "_" + pageNum + "_" + pageSize;
String jsonString = jedis.get(key);
// 2.如果有缓存,重置过期时间并返回
if (jsonString != null) {
jedis.expire(key, 3600L);
return JSON.parseObject(jsonString, Page.class);
}
// 3.如果没有缓存,访问数据库
Page page = xxxMapper.page(pageNum, pageSize);
// 4.将查询结果缓存
jedis.setex(key, 3600L, page);
// 5.返回结果
return page;
}
}
3 直接在业务代码中处理缓存存在的问题 #
- 代码繁琐
- 代码入侵严重
- 缓存的流程基本雷同,在多个方法中添加缓存操作,需要重复写相似的代码
4 解决思路:AOP + 自定义注解 #
- 自定义一种注解,在需要对返回值进行缓存的方法上添加该注解。
- 定义一个切面,对被注解的方法环绕通知。
- 在环绕通知中,方法执行前,以全限类名+方法名+方法参数的 JSON 字符串作为 key,查询缓存,若存在则直接返回,否则执行方法,然后将方法返回的结果缓存,最后返回结果。
5 代码实现 #
5.1 自定义注解 #
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCacheable {
// 缓存过期时间,默认1小时
long expireSeconds() default 3600L;
}
5.2 定义切面 #
@Aspect
@Component
public class CacheableAspect {
@Autowired
private Jedis jedis;
@Around("@annotation(com.xxx.annotation.MyCacheable)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 入参
Object args = joinPoint.getArgs();
// 方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod;
// 用全限类名+方法名+参数JSON作为key
String key = method.getDeclaringClass().getName() + "." + JSON.toJSONString(args);
String jsonString;
try {
jsonString = jedis.get(key);
} catch(Exception e) {
// 万一Redis挂了,直接执行原来的方法返回
return joinPoint.proceed();
}
if (jsonString != null) {
// 命中缓存
// 重新设置过期时间
jedis.expire(key, 3600L);
// 获取注解所在方法返回值类型
Class returnType = signature.getReturnType();
// 返回结果
return JSON.parseObject(jsonString, returnType);
}
// 未命中缓存,执行原方法,获得返回结果
Object result = joinPoint.proceed();
// 获取注解中的过期时间
MyCacheable cacheable = method.getAnnotation(MyCacheable.class);
// 将结果缓存
jedis.setex(key, cacheable.expireSeconds, JSON.toJSONString(result));
return result;
}
}
5.3 业务代码 #
@Service
public class XxxServiceImpl implements XxxService {
@Autowired
private XxxMapper xxxMapper;
// 优雅永不过时
@MyCacheable
@Override
public Page page(Integer pageNum, Integer pageSize) {
return xxxMapper.page(pageNum, pageSize);
}
}