通过注解实现自动使用 HotKey 探测并缓存
首先我们要定义注解 AutoHotKey:
▼java复制代码@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoHotKey { @NotNull String keyPrefix() default ""; @NotNull Class<?> valueType(); }
需要的参数是 key 的前缀,以及结果的类型(方便做类型的转换),使用的目标是方法,使用时机则自然是运行时。
然后进行 AOP 编程,进行环绕通知:
实现的困难点就是如何获取 id 作为 key 的后缀。
▼java复制代码private Long getId(Object[] args) { for (Object arg : args) { if (arg != null) { Long id = getIdFromArg(arg); if (id != null && id >= 0) { return id; } } } return null; } private Long getIdFromArg(Object arg) { if (arg instanceof Long) { return (Long) arg; } Long id = null; if (arg != null) { try { // 使用getId()方法获取id Method getIdMethod = arg.getClass().getMethod("getId"); Object idValue = getIdMethod.invoke(arg); if (idValue instanceof Long) { id = (Long) idValue; } if (id != null && id > 0) { return id; } } catch (NoSuchMethodException e) { return null; } catch (Exception e) { throw new BusinessException(ErrorCode.SYSTEM_ERROR); } } return id; }
[!IMPORTANT]
没有做过多的校验,只能祈祷开发者传入的参数中有
getId()方法,而且第一个有getId()方法的参数就是我需要的那一个。(或者参数本身就是Long类型),所以只适用于以Long类型参数 为 key 后缀 的时机。(要做其它的校验也可以,有点懒 orz)
最后完整的实现是这样的;
▼java复制代码@Aspect @Component @Slf4j public class HotKeyInterceptor { @Around("@annotation(autoHotKey)") public Object autoHotKey(ProceedingJoinPoint proceedingJoinPoint, AutoHotKey autoHotKey) throws Throwable { Object[] args = proceedingJoinPoint.getArgs(); Long id = getId(args); ThrowUtils.throwIf(id == null, ErrorCode.SYSTEM_ERROR, "id 未初始化"); String key = autoHotKey.keyPrefix() + id; Object value = JdHotKeyStore.getValue(key); Class<?> valueType = autoHotKey.valueType(); if (valueType.isInstance(value)) { return ResultUtils.success(valueType.cast(value)); } else if (value != null) { throw new BusinessException(ErrorCode.SYSTEM_ERROR, "需要的缓存类型与设置的不匹配"); } Object result = proceedingJoinPoint.proceed(); if (result instanceof BaseResponse) { Object data = ((BaseResponse<?>) result).getData(); if (valueType.isInstance(data)) { JdHotKeyStore.smartSet(key, data); } } return result; } private Long getId(Object[] args) { for (Object arg : args) { if (arg != null) { Long id = getIdFromArg(arg); if (id != null && id >= 0) { return id; } } } return null; } private Long getIdFromArg(Object arg) { if (arg instanceof Long) { return (Long) arg; } Long id = null; if (arg != null) { try { // 使用getId()方法获取id Method getIdMethod = arg.getClass().getMethod("getId"); Object idValue = getIdMethod.invoke(arg); if (idValue instanceof Long) { id = (Long) idValue; } if (id != null && id > 0) { return id; } } catch (NoSuchMethodException e) { return null; } catch (Exception e) { throw new BusinessException(ErrorCode.SYSTEM_ERROR); } } return id; } }
最后我们分别在 getQuestionBankVOById 方法以及 getQuestionVOById 方法进行试验:(一个是以带 getId() 方法的对象作为参数,一个参数本身就是 Long 类型)
▼java复制代码@GetMapping("/get/vo") @AutoHotKey(keyPrefix = "bank_detail_", valueType = QuestionBankVO.class) public BaseResponse<QuestionBankVO> getQuestionBankVOById(QuestionBankQueryWithQuestionRequest questionBankQueryWithQuestionRequest, HttpServletRequest request) { ThrowUtils.throwIf(questionBankQueryWithQuestionRequest == null || questionBankQueryWithQuestionRequest.getId() <= 0, ErrorCode.PARAMS_ERROR); // 查询数据库 long id = questionBankQueryWithQuestionRequest.getId(); QuestionBank questionBank = questionBankService.getById(id); ThrowUtils.throwIf(questionBank == null, ErrorCode.NOT_FOUND_ERROR); // 获取封装类 QuestionBankVO questionBankVO = questionBankService.getQuestionBankVO(questionBank, request); if (questionBankQueryWithQuestionRequest.isNeedToListQuestion()) { QuestionQueryRequest questionQueryRequest = new QuestionQueryRequest(); questionQueryRequest.setQuestionBankId(id); questionQueryRequest.setPageSize(questionBankQueryWithQuestionRequest.getPageSize()); questionQueryRequest.setCurrent(questionBankQueryWithQuestionRequest.getCurrent()); Page<Question> questionPage = questionService.getQuestionPage(questionQueryRequest); Page<QuestionVO> questionVOPage = questionService.getQuestionVOPage(questionPage, request); questionBankVO.setQuestionVOPage(questionVOPage); } return ResultUtils.success(questionBankVO);
▼java复制代码@GetMapping("/get/vo") @AutoHotKey(keyPrefix = "question_detail_", valueType = QuestionVO.class) public BaseResponse<QuestionVO> getQuestionVOById(Long id, HttpServletRequest request) { ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR); // 查询数据库 Question question = questionService.getById(id); ThrowUtils.throwIf(question == null, ErrorCode.NOT_FOUND_ERROR); // 获取封装类 return ResultUtils.success(questionService.getQuestionVO(question, request)); }
最后的结果如下:
能够正常使用,同时又实现了对热门题目的自动缓存,一箭双雕^v^
评论
问答助学
相关内容
0个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
