java自定义注解,实现幂等

幂等:多次操作和一次操作是一样的结果,例如注册的时候,同一个手机号注册多次,但最终数据库只会保存一个手机号。

实现幂等,可能会有多种手段

1、数据库使用唯一索引;

2、使用java技术:① springAOP的方式;② springboot的拦截器实现;

那我们这里就使用拦截器技术实现。

这里需要使用到redis,前提是已经引入redis的依赖和配置,表示能正常使用

1、自定义注解对象创建;

2、对应拦截器进行业务判断,这里我们不用前端给我们的传递给我们的唯一标识符,因为具有依赖性,我们后台直接使用用户id+入参,再进行MD5加密存储生成的唯一性key的思路,最后配置拦截器,让其生效,然后生成的注解添加到对应注解上,通过对应新增的操作我们需要加上该注解,而且是会加上过期时间。也就是在这个期间范围内,是不允许多次重复发送这样的请求的。

具体代码如下:

1、创建注解类:

/**
 * 自定义幂等性判断注解
 */
@Target(ElementType.METHOD)  //作用于方法
@Retention(RetentionPolicy.RUNTIME)  // 运行时生效
public @interface IdEmInterface {
    /**
     * 幂等性判断时间
     * @return
     */
    int time() default 60; // 默认60秒

     /**
     * 单位
     * @return
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS; //默认秒

}

2、具体业务实现

@Component
public class IdEmInterceptor implements HandlerInterceptor {  //实现拦截器接口

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private RedisTemplate redisTemplate;



    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        IdEmInterface annotation = null;
        try {
            Method method = ((HandlerMethod) handler).getMethod();
            annotation = method.getAnnotation(IdEmInterface.class);
        }catch (Exception e){}
        if (annotation != null) {
            String id = creatId(request);
            if (redisTemplate.opsForValue().get(id) != null){
                response.getWriter().write("Please do not resubmit~");
                return false;
            }else {
                redisTemplate.opsForValue().set(id, "true",annotation.time(), annotation.timeUnit());  //这里我们只是使用他的key,所以value不重要
                return true;
            }
        }
        return HandlerInterceptor.super.preHandle(request, response, handler);

    }

    private String creatId(HttpServletRequest request) throws JsonProcessingException {
        Long uid = 0L;
        SecurityUserDetails securityUserDetails = SecurityUserInfoUtil.getSecurityUserDetails();
        if (securityUserDetails != null){
            uid = securityUserDetails.getUid();
        }
        String requestParam = objectMapper.writeValueAsString(request.getParameterMap());
        String id = SecureUtil.md5(uid + requestParam);  //使用MD5加密
        return id;
    }

}

3、拦截器添加我们定义的拦截规则

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Resource
    private IdEmInterceptor idEmInterceptor;

    @Override
    public void addInterceptors(org.springframework.web.servlet.config.annotation.InterceptorRegistry registry) {
        registry.addInterceptor(idEmInterceptor).addPathPatterns("/**");
    }

    /**
     * 这里讲一下流程
     * 1、先去自定义一个注解
     * 2、对注解进行一个拦截规则设置
     * 3、实现拦截器,拦截规则过滤
     *
     */
    
}

4、在业务代码中添加注解

    @RequestMapping("/add")
    @IdEmInterface   // 添加响应注解
    public ResponseEntity  add(@Validated Discuss discuss){
        discuss.setUid(SecurityUserInfoUtil.getSecurityUserDetails().getUid());
        boolean save = discussService.save(discuss);
        if (save){
            return ResponseEntity.success(save);
        }
        return ResponseEntity.error("请求失败!");
    }

那么默认60秒内请求多次就会提示请勿重复请求,这样就能在一定程度上控制请求,防止同一个用户的同一个入参在某个时间内多次重复提交。

 

posted @ 2025-06-17 22:14  多多指教~  阅读(98)  评论(0)    收藏  举报