自定义幂等性校验之annotation+reflection+interceptor练习

通过springboot+自定义注解+反射+拦截器+token机制进行幂等性校验。

先请求获取token,并将此token放入redis中,并设置1秒过期时间。然后请求新增订单接口,被拦截器拦截,通过反射获取注解类,有该注解的方法进行幂等性校验。

代码示例:

/**
 * 幂等性方法注解
 * 此幂等性注解校验为无参,通过token(设置过期时间1秒)机制判断是否重复提交
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
    //String id();
    //Class<?> lockClass();
}
@Component
public class IdempotentInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        //获取方法上的注解
        Idempotent annotation = handlerMethod.getMethodAnnotation(Idempotent.class);
        if (annotation != null) {
            //幂等性校验
            System.out.println("幂等性校验");
            String token = request.getHeader("Authorization");
            if (StringUtils.isEmpty(token)) {
                System.out.println("token为空");
                return false;
            }
        }
        return true;
    }
}
@Component
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private IdempotentInterceptor idempotentInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(idempotentInterceptor).addPathPatterns("/**");
    }
}
@Idempotent
    @RequestMapping("addUser7")
    public String addUser(String id) {
        //业务代码
        System.out.println("添加用户ID:" + id);
        return "请求成功";
    }

或通过aop切面进行判断

@Component
@Aspect
public class IdempotentAspect {

    @Around("@annotation(com.ldc.springboot_idempotent.annotation.Idempotent)")
    public Object idempotent(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();

        //前置增强
        System.out.println("前置增强操作");
        String token = request.getHeader("Authorization");
        if (StringUtils.isEmpty(token)) {
            System.out.println("token为空");
            return "token为空";
        }
        //IdempotentUtil.check();
        Object rs = pjp.proceed();
        //后置增强
        System.out.println("后置增强操作");
        return rs;
        //此aop切面处理有问题,改用拦截器处理
    }
}

通过aop方式代码示例:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
    String id();
    Class<?> lockClass();
}

@Component
@Aspect
public class IdempotentAspect {

    @Around("@annotation(com.ldc.springboot_idempotent.annotation.Idempotent)")
    public Object idempotent(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //获取参数
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = pjp.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());
        Idempotent annotation = method.getAnnotation(Idempotent.class);
        String id = annotation.id();
        Class<?> lockClass = annotation.lockClass();
        //解析springEL表达式
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(id);
        StandardEvaluationContext context = new StandardEvaluationContext();
        //添加参数
        Object[] args = pjp.getArgs();
        DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
        String[] parameterNames = discoverer.getParameterNames(method);
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i],args[i].toString());
        }
        //解析参数
        String param = expression.getValue(context).toString();
        //前置增强
        System.out.println("前置增强操作");
        boolean check = IdempotentUtil.check(param, lockClass);
        if (!check) {
            return "请勿重复提交...";
        }
        Object rs = pjp.proceed();
        //后置增强
        System.out.println("后置增强操作");
        return rs;
        //此aop切面处理有问题,改用拦截器处理
        //此问题通过springEL表达式获取动态参数解决,再结合幂等性工具进行判断
    }
}
public class IdempotentUtil {
    //根据LRU算法淘汰最近最少使用的数据的Map集合,最大容量100个
    private static LRUMap<String, Integer> lruMap = new LRUMap<>(100);

    /**
     * 幂等性判断
     */
    public static boolean check(String id, Object lockClass) {
        synchronized (lockClass) {
            //重复请求判断
            if (lruMap.containsKey(id)) {
                //重复请求
                System.out.println("请勿重复提交..." + id);
                return false;
            }
            //非重复请求,存储ID
            lruMap.put(id, 1);
        }
        return true;
    }
}
@RestController
public class DemoController7 {

    @Idempotent(id = "#id",lockClass = DemoController7.class)
    @RequestMapping("addUser8")
    public String addUser8(String id) {
        //业务代码
        System.out.println("添加用户ID:" + id);
        return "请求成功";
    }

}

以上幂等性判断未实现,需结合redis进行实现,第一遍请求判断是否存在该id,若不存在则将id放入redis中,正常执行业务;若存在则返回请勿重复提交,整体思路如此,后续需深入理解注解的应用及模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值