(1)定义一个注解,用来标识需要缓存方法。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
@Documented
public @interface CachedInRequest {
}
(2)定义一个缓存 key,用作被缓存的方法的唯一标识。
@EqualsAndHashCode
public class InvocationTarget {
private final Class targetClass; // 缓存方法所在的类
private final String targetMethod; // 缓存方法的方法名
private final Object[] args; // 调用方法时传入的参数
public InvocationTarget(ProceedingJoinPoint joinPoint) {
this.targetClass = joinPoint.getSignature().getDeclaringType();
this.targetMethod = joinPoint.getSignature().getName();
this.args = joinPoint.getArgs();
}
@Override
public String toString() {
return String.format("%s.%s(%s)", targetClass.getName(), targetMethod, Arrays.toString(args));
}
}
(3)新增一个缓存管理类,用来维护缓存内容。
/**
* @RequestScope
* 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。
* 也就是说,每个 HTTP 请求拥有一个被创建的 bean 实例。
*/
@Component
@RequestScope
public class RequestCacheManager {
private final Map<InvocationTarget, Object> cache = new ConcurrentHashMap<>();
public Optional<Object> get(InvocationTarget invocationContext) {
return Optional.ofNullable(cache.get(invocationContext));
}
public void put(InvocationTarget methodInvocation, Object result) {
cache.put(methodInvocation, result);
}
}
(4)通过 AOP 切面拦截需要缓存的方法,对其进行缓存功能增强。
@Aspect
@Component
@RequiredArgsConstructor
public class CacheInRequestAspect {
private final RequestCacheManager requestCacheManager;
@Around("@annotation(CachedInRequest)")
public Object requestCache(ProceedingJoinPoint joinPoint) throws Throwable {
// 1. 从 ProceedingJoinPoint 中获取需要缓存方法的 key 对象
InvocationTarget invocationTarget = new InvocationTarget(joinPoint);
// 2. 如果缓存中已经存在结果,则直接返回,不需要调用目标方法
Optional<Object> cachedResult = requestCacheManager.get(invocationTarget);
if (cachedResult.isPresent()) {
return cachedResult.get();
}
// 3. 缓存中不存在结果,则调用目标方法
Object methodResult = joinPoint.proceed();
// 4. 把方法调用结果存入到缓存中
requestCacheManager.put(invocationTarget, methodResult);
return methodResult;
}
}
(5)功能测试
Service
@Service
@Slf4j
public class CacheDemoService {
@SneakyThrows
public int query() {
// 模拟业务逻辑
Thread.sleep(1 * 1000);
return 1;
}
@CachedInRequest
public int queryCache() {
return query();
}
}
Controller
@RestController
@Slf4j
@RequiredArgsConstructor
public class DemoController {
private final CacheDemoService cacheDemoService;
@GetMapping("/query")
public String query(@RequestParam(value = "count", defaultValue = "5") int count) {
log.info("query 请求开始");
for (int i = 0; i < count; ++ i) {
log.info("cacheDemoService.query(): {}", cacheDemoService.query());
}
return "done";
}
@GetMapping("/queryCache")
public String queryCache(@RequestParam(value = "count", defaultValue = "5") int count) {
log.info("queryCache 请求开始");
for (int i = 0; i < count; ++ i) {
log.info("cacheDemoService.queryCache(): {}", cacheDemoService.queryCache());
}
return "done";
}
}
调用 http://localhost:8080/query
接口,控制台输出:
2021-08-27 13:00:59.516 INFO 22452 --- [nio-8080-exec-2] com.example.demo.DemoController : query 请求开始
2021-08-27 13:01:00.600 INFO 22452 --- [nio-8080-exec-2] com.example.demo.DemoController : cacheDemoService.query(): 1
2021-08-27 13:01:01.613 INFO 22452 --- [nio-8080-exec-2] com.example.demo.DemoController : cacheDemoService.query(): 1
2021-08-27 13:01:02.624 INFO 22452 --- [nio-8080-exec-2] com.example.demo.DemoController : cacheDemoService.query(): 1
2021-08-27 13:01:03.629 INFO 22452 --- [nio-8080-exec-2] com.example.demo.DemoController : cacheDemoService.query(): 1
2021-08-27 13:01:04.641 INFO 22452 --- [nio-8080-exec-2] com.example.demo.DemoController : cacheDemoService.query(): 1
每隔一秒打印一次日志,说明没有被缓存。
调用 http://localhost:8080/queryCache
接口,控制台输出:
2021-08-27 13:01:18.311 INFO 22452 --- [nio-8080-exec-3] com.example.demo.DemoController : queryCache 请求开始
2021-08-27 13:01:19.336 INFO 22452 --- [nio-8080-exec-3] com.example.demo.DemoController : cacheDemoService.queryCache(): 1
2021-08-27 13:01:19.337 INFO 22452 --- [nio-8080-exec-3] com.example.demo.DemoController : cacheDemoService.queryCache(): 1
2021-08-27 13:01:19.337 INFO 22452 --- [nio-8080-exec-3] com.example.demo.DemoController : cacheDemoService.queryCache(): 1
2021-08-27 13:01:19.337 INFO 22452 --- [nio-8080-exec-3] com.example.demo.DemoController : cacheDemoService.queryCache(): 1
2021-08-27 13:01:19.337 INFO 22452 --- [nio-8080-exec-3] com.example.demo.DemoController : cacheDemoService.queryCache(): 1
第一次方法执行耗时 1s
,后续日志间隔时间非常短,说明后续的接口都是走的缓存。
网友评论