通过注解实现自动使用 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)); }

最后的结果如下:

image.png 能够正常使用,同时又实现了对热门题目的自动缓存,一箭双雕^v^

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
HANAdes
下载 APP