spEl解析

发布时间 2023-08-08 14:29:53作者: 久坐不移

前言

  有个需求要求做幂等,按之前的做法,在业务代码中使用 redis 根据幂等标识获取值,有则证明该入参短时间内已触发过,滤过。然后再业务结尾,将幂等标识发送个redis

  注:以上幂等有个问题,两个相同的入参同时进入,同样再redis上是找不到幂等标识的,此时第一个入参没有将幂等标识发送到redis上 第二个入参同样也能进入业务代码,会有巨大的问题,所以,基于该问题的考虑,我决定使用redis分布式锁,来做幂等

  这段代码与业务代码其实毫无关联,趁着开发时间有余,把这一块封装出来使用aop完成。

  此时需要一个注解,需要从方法的入参中拿到需要幂等标识,如果写死,这个注解的通用性就此吃了屎,所以想到使用spEl去取,入参中的值,spring的部分注解是自动支持spEl取值的,自定义的注解就别想了,需要自己手动搞。

代码

幂等注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
    String key();
}

aop实现

    @Around("idempotentPointCut() && @annotation(idempotent)")
    public Object around(ProceedingJoinPoint point, Idempotent idempotent) throws Throwable {
        String key = "Idempotent" + parseSpEl(point, idempotent.key());
        RLock lock = redissonClient.getLock(key + "lock");
        try {
            if (lock.tryLock(30, 60,TimeUnit.SECONDS)){
                return point.proceed();
            }
        } catch (Throwable e) {
            log.error("获取分布式锁过程报错!!", e);
            lock.unlock();
            throw e;
        } finally {
            lock.unlock();
        }
    }

    private Object parseSpEl(ProceedingJoinPoint point, String key) {
        MethodSignature signature = (MethodSignature) point.getSignature();
        List<String> paramNameList = Arrays.asList(signature.getParameterNames());
        List<Object> paramList = Arrays.asList(point.getArgs());

        //将方法的参数名和参数值一一对应的放入上下文中
        EvaluationContext ctx = new StandardEvaluationContext();
        for (int i = 0; i < paramNameList.size(); i++) {
            ctx.setVariable(paramNameList.get(i), paramList.get(i));
        }

        // 解析SpEL表达式获取结果
        return spelParser.parseExpression(key).getValue(ctx);
    }