美文网首页
利用 @RequestScope 进行 HTTP 请求级别的缓存

利用 @RequestScope 进行 HTTP 请求级别的缓存

作者: 蓝笔头 | 来源:发表于2021-08-28 21:41 被阅读0次

(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,后续日志间隔时间非常短,说明后续的接口都是走的缓存。

参考

相关文章

  • 利用 @RequestScope 进行 HTTP 请求级别的缓存

    (1)定义一个注解,用来标识需要缓存方法。 (2)定义一个缓存 key,用作被缓存的方法的唯一标识。 (3)新增一...

  • Android_图片加载库

    请求分发模块。负责封装请求,对请求进行优先级排序,并按照类型进行分发。 缓存模块。通常包括一个二级的缓存,内存缓存...

  • 浏览器缓存原理

    背景:http1中利用缓存机制可以节约http请求数量,减少请求数据 弱缓存(协商缓存) 1.第一次请求,服务端通...

  • Bitmap优化

    网络的优化 1.请求的缓存。Http请求是可以做缓存的。 三级缓存内存缓存、外部缓存算法LruCache+自定的 ...

  • 关于前端缓存的笔记

    前端缓存分类 HTTP缓存(指HTTP请求时候用到的缓存,主要在服务器端进行设置)浏览器端的缓存(本地缓存技术) ...

  • APP开发实战105-缓存控制

    27.3缓存控制 1服务端控制缓存 A 利用HTTP协议的头字段 如volley请求库,便是通过“Cache-Co...

  • HTTP缓存

    1.HTTP缓存分类 强缓存: 使用本地缓存,不请求服务器。可以使用cache-control和expires进行...

  • Volley源码解析

    Volley的优缺点 优点 自动的调度网络请求 多并发的网络请求 可以缓存http请求 支持请求的优先级 支持取消...

  • web前端页面优化方法

    1.加载优化 (1)减少http请求,尽量减少向服务器的请求数量 (2)利用缓存:所有的资源都要的服务器端设置缓存...

  • web性能优化

    减少HTTP请求、合并文件、利用css sprite 减少DNS查找 避免重定向 响应时间,使用ajax进项缓存,...

网友评论

      本文标题:利用 @RequestScope 进行 HTTP 请求级别的缓存

      本文链接:https://www.haomeiwen.com/subject/yhdmiltx.html