文章目录
- 设置缓存
- 1、定义注解
- 2、AOP
- 3、测试
设置缓存
1、定义注解
注解定义四个属性,分别是:
- value,key的别名
- key : redis的key,如果key不设置,则会用方法名加参数列表作为key
- expire:失效时间,默认为 1天
- TimeUnit : 时间单位,默认为秒
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCache {
@AliasFor("key")
String value() default "";
String key() default "";
long expire() default 24 * 60 * 60L;
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
2、AOP
这个Aop的作用:
- 查redis缓存
- 设置缓存-通过线程池异步设置缓存
里面用到了fastJson、lombok
依赖
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
import com.alibaba.fastjson.JSON;
import com.sifan.erp.annotation.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Component
@Aspect
@Slf4j
public class RedisCacheAOP {
@Resource
private RedisTemplate redisTemplate;
/**
* 线程池,目标方法执行完之后直接返回结果,设置缓存的工作异步执行,这样能提高运行效率*
*/
private static ThreadPoolExecutor threadPoolExecutor;
static {
// 初始化线程池
threadPoolExecutor = new ThreadPoolExecutor(1, 5, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));
}
/**
* 切入点 @RedisCache注解作为切入点*
*/
@Pointcut("@annotation(com.sifan.erp.annotation.RedisCache)")
public void RedisCacheAOP() {
}
@Around("RedisCacheAOP()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取方法签名
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 获取方法上的@RedisCache注解
RedisCache redisCacheAnnotation = methodSignature.getMethod().getAnnotation(RedisCache.class);
// 判断key或value是否为空,有一个不为空则作为redis的key,如果都存在则取key的值
String redisKey = null;
String key = redisCacheAnnotation.key();
String value = redisCacheAnnotation.value();
if (value != null && !"".equals(value)) {
redisKey = value;
}
if (key != null && !"".equals(key)) {
redisKey = key;
}
// AOP返回的结果
Object result = null;
// 到这如果redisKey为null,没进上面的两个判断,表示key与value为空,使用方法名与参数列表作为redisKey
if (redisKey == null) {
String methodName = methodSignature.getName();
int index = methodName.indexOf('$');
if (index == -1) {
index = methodName.lastIndexOf('.');
}
methodName = methodName.substring(index + 1);
// 获取参数列表
Object[] args = joinPoint.getArgs();
if (args != null) {
StringBuilder sb = new StringBuilder();
sb.append(methodName);
sb.append(":");
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
// 把参数转为string类型,有些参数是对象
String stringArg = JSON.toJSONString(arg);
sb.append(stringArg);
// 每个参数分割开
if (i != args.length - 1) {
sb.append(":");
}
}
redisKey = sb.toString();
}
}
// ok 现在key搞定了
// 判断key在redis是否存在值,存在直接返回结果,不执行目标方法
Object redisRes = redisTemplate.opsForValue().get(redisKey);
log.info("缓存key = {}", redisKey);
if (redisRes != null) {
// 如果redis有值,直接返回
log.info("获取redis缓存成功 key = {}", redisKey);
result = redisRes;
} else {
// 没有值,执行目标方法,设置值,返回
Object proceed = joinPoint.proceed();
result = proceed;
Long expire = redisCacheAnnotation.expire();
TimeUnit timeUnit = redisCacheAnnotation.timeUnit();
// copyRedisKey 必须final修饰,因为异步执行需要copyRedisKey,线程池会复制一份到execute中,这时会出现两份copyRedisKey,线程池为了保存两份copyRedisKey一致,必须用fina修饰
final String copyRedisKey = redisKey;
// 异步设置缓存
threadPoolExecutor.execute(() -> {
redisTemplate.opsForValue().set(copyRedisKey, proceed, expire, timeUnit);
log.info("异步设置缓存成功 key = {}", copyRedisKey);
});
}
return result;
}
}
3、测试
在Service中定义一个redisCacheService方法,加上注解@RedisCache
1、测试简单参数,简单结果
@RedisCache
public String redisCacheService(Integer id, String name) {
return "我是缓存";
}
测试类
@Resource
private UserServiceImpl userService;
@Test
public void testRedisCache_1() {
String res = userService.redisCacheService(100, "二狗");
System.out.println(res);
}
结果
第一次执行方法,异步设置缓存
第二次执行方法,直接获取缓存
2、测试复杂参数,复杂结果
当然一般需要设置的缓存的方法的参数是简单参数直接使用 @RedisCache(expire = 60)即可,expire 设置失效时间,单位默认为秒
3、自己设置key
@RedisCache(key = "userCache")