手动实现一个缓存抽象
程序员文章站
2022-04-23 23:49:44
...
手写一个 redis 缓存抽象
下面是实现 SPEL表达式的代码, spring-cache 的 缓存抽象没法自定义时间,所以自己实现了一下普通的缓存注解
/**
* 用于SpEL表达式解析.
*/
private static SpelExpressionParser parser = new SpelExpressionParser();
/**
* 用于获取方法参数定义名字.
*/
private static DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
public static String generateKeyBySpEL(String spELString, ProceedingJoinPoint joinPoint) {
// 通过joinPoint获取被注解方法
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
// 使用spring的DefaultParameterNameDiscoverer获取方法形参名数组
String[] paramNames = nameDiscoverer.getParameterNames(method);
// 解析过后的Spring表达式对象
Expression expression = parser.parseExpression(spELString);
// spring的表达式上下文对象
EvaluationContext context = new StandardEvaluationContext();
// 通过joinPoint获取被注解方法的形参
Object[] args = joinPoint.getArgs();
// 给上下文赋值
for(int i = 0 ; i < args.length ; i++) {
context.setVariable(paramNames[i], args[i]);
}
// 表达式从上下文中计算出实际参数值
/*如:
@annotation(key="#student.name")
method(Student student)
那么就可以解析出方法形参的某属性值,return “xiaoming”;
*/
return expression.getValue(context).toString();
}
WebUtil 类的静态方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheResult {
Unit unit() default Unit.SECOND;
int duration() default 3*60;//缓存3分钟
String spel();
}
自定义缓存注解
public enum Unit {
/**
* second
*/
SECOND,
/**
* 小时
*/
HOUR,
/**
* 分钟
*/
MINUTE,
/**
* 一天
*/
Day,
}
@Aspect
@Component
// @Order(-20)
@Slf4j
public class RedisCacheAspect {
@Pointcut("@annotation(CacheResult)")
public void aop() {
// log.info("args-> {}", Arrays.toString(pjp.getArgs()));
}
@Pointcut("@annotation(UpdateCache)")
public void aopForUpdate() {
// log.info("args-> {}", Arrays.toString(pjp.getArgs()));
}
@Around("aop()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
CacheResult cacheResult = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(CacheResult.class);
String key = WebUtil.generateKeyBySpEL(cacheResult.spel(), joinPoint);
log.info("cache Key {}",key);
Object cache = redisTemplate.opsForValue().get(key);
if (cache != null) {
//如果缓存有,就返回
return cache;
}
//没有缓存,运行,然后缓存入 redis中
Object obj = joinPoint.proceed(joinPoint.getArgs());
long ttl = cacheResult.duration();
Duration duration = null;
switch (cacheResult.unit()) {
case SECOND: {
duration = Duration.ofSeconds(ttl);
break;
}
case MINUTE: {
duration = Duration.ofMinutes(ttl);
break;
}
case HOUR: {
duration = Duration.ofHours(ttl);
break;
}
default: {
//一天时间
duration = Duration.ofDays(ttl);
break;
}
}
redisTemplate.opsForValue().set(key, obj, duration);
return obj;
}
@Around("aopForUpdate()")
public Object delCache(ProceedingJoinPoint joinPoint) throws Throwable {
UpdateCache updateCache = ((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(UpdateCache.class);
String key = WebUtil.generateKeyBySpEL(updateCache.spel(),joinPoint);
expireCache(updateCache,key);
return joinPoint.proceed(joinPoint.getArgs());
}
@Async
public void expireCache(UpdateCache updateCache, String key) {
long ttl = updateCache.duration();
Duration duration = null;
switch (updateCache.unit()) {
case SECOND: {
duration = Duration.ofSeconds(ttl);
break;
}
case MINUTE: {
duration = Duration.ofMinutes(ttl);
break;
}
case HOUR: {
duration = Duration.ofHours(ttl);
break;
}
default: {
//一天时间
duration = Duration.ofDays(ttl);
break;
}
}
redisTemplate.expire(key,duration);
}
@Resource
private RedisTemplate<String, Object> redisTemplate;
}