需求
实现自定义注解动态赋值,针对请求传输过来参数进行动态赋值,拦截处理redis缓存中文章的阅读浏览量。。直接上代码
一、自定义注解类
代码如下(示例):
/**
* created By gywenlover on 2020-08-11
*
* 该注解作用在方法上,需要传入动态el值进行阅读量增加(el内容为el表达式)
*
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadNum {
String front() default "";
String el() default "";
String key() default "";
}
二、请求方法添加注解类
1.请求方法添加注解
代码如下(示例):
@GetMapping(value = "/{id}")
@ApiOperation("获取活动详情 - 需要活动id")
@ReadNum(front = ReadCode.ACTIVITY_KEY_CODE,el = "#id")
public AjaxResult getInfo(@PathVariable("id") String id) {
return null;
}
三、AOP拦截器
1.请求方法添加注解
代码如下(示例):
/**
* @ClassName: viewsAspect
* @description: 甘孜州自建频道各模块浏览量统计
* @author: Apache_MYK
* @createDate: 2021/8/5 10:06
* @version: 1.0
*/
@Aspect
@Component
public class ViewsAspect {
private static final Logger log = LoggerFactory.getLogger(ViewsAspect.class);
@Autowired
private RedisCache redisCache;
// 配置织入点
// @Pointcut(
// "execution(* com.api.controller..GzActivityController.getInfo(String))" +
// "|| execution(* com.api.controller..GzWorkController.getInfo(Long))" +
// "|| execution(* com.api.controller..GzCurriculumController.getInfo(String))"+
// "|| execution(* com.api.controller..GzAnnouncementController.getAnnouncementInfo(*))"
// )
@Pointcut("@annotation(com.api.common.annotation.ReadNum)")
public void viewsPointCut() {
}
/**
* 只有正常返回才会执行此方法
* 如果程序执行失败,则不执行此方法
*/
@AfterReturning(pointcut = "viewsPointCut()")
public void doAfterReturning(JoinPoint joinPoint) throws Exception {
//获取注解
ReadNum controllerLog = getAnnotationReadNum(joinPoint);
String viewType = controllerLog.front()+controllerLog.key();
Integer cacheObject = redisCache.getCacheObject(viewType);
if (Objects.isNull(cacheObject)) {
cacheObject = 0;
}
redisCache.setCacheObject(viewType, cacheObject + 1);
}
/**
* 是否存在注解,如果存在就获取
*/
private ReadNum getAnnotationReadNum(JoinPoint joinPoint) throws Exception {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
if (method != null) {
return method.getAnnotation(ReadNum.class);
}
return null;
}
private SpelExpressionParser spelParser = new SpelExpressionParser();
/**
* 前置通知
*/
@Before("@annotation(com.api.common.annotation.ReadNum)")
public void before(JoinPoint joinPoint) throws Exception {
ReadNum readNum = getAnnotationReadNum(joinPoint);
//获取方法的参数名和参数值
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
List<String> paramNameList = Arrays.asList(methodSignature.getParameterNames());
List<Object> paramList = Arrays.asList(joinPoint.getArgs());
//将方法的参数名和参数值一一对应的放入上下文中
EvaluationContext ctx = new StandardEvaluationContext();
for (int i = 0; i < paramNameList.size(); i++) {
ctx.setVariable(paramNameList.get(i), paramList.get(i));
}
// 解析SpEL表达式获取结果
String value = spelParser.parseExpression(readNum.el()).getValue(ctx).toString();
//获取 sensitiveOverride 这个代理实例所持有的 InvocationHandler
InvocationHandler invocationHandler = Proxy.getInvocationHandler(readNum);
// 获取 invocationHandler 的 memberValues 字段
Field hField = invocationHandler.getClass().getDeclaredField("memberValues");
// 因为这个字段是 private final 修饰,所以要打开权限
hField.setAccessible(true);
// 获取 memberValues
Map memberValues = (Map) hField.get(invocationHandler);
// 修改 value 属性值
memberValues.put("key", value);
}
}