Spring Boot 使用 AOP 防止重复提交

沙海 2021年6月10日03:52:02Java评论28字数 4607阅读15分21秒阅读模式
摘要

智能摘要

智能摘要文章源自JAVA秀-https://www.javaxiu.com/31015.html

页面提交请求携带这个提交令牌,后端验证并在第一次验证后删除该令牌,保证提交请求的唯一性。上述的思路其实没有问题的,但是需要前后端都稍加改动,如果在业务开发完在加这个的话,改动量未免有些大了,本节的实现方案无需前端配合,纯后端处理。欢迎加入我的知识星球,一起探讨架构,交流源码。文章源自JAVA秀-https://www.javaxiu.com/31015.html

原文约 2959 | 图片 7 | 建议阅读 6 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/31015.html

Spring Boot 使用 AOP 防止重复提交

点击关注 ? Java基基 文章源自JAVA秀-https://www.javaxiu.com/31015.html

收录于话题文章源自JAVA秀-https://www.javaxiu.com/31015.html

#Java基基文章源自JAVA秀-https://www.javaxiu.com/31015.html

145个文章源自JAVA秀-https://www.javaxiu.com/31015.html

点击上方“Java基基”,选择“设为星标”文章源自JAVA秀-https://www.javaxiu.com/31015.html

做积极的人,而不是积极废人!文章源自JAVA秀-https://www.javaxiu.com/31015.html

文章源自JAVA秀-https://www.javaxiu.com/31015.html

源码精品专栏文章源自JAVA秀-https://www.javaxiu.com/31015.html

 文章源自JAVA秀-https://www.javaxiu.com/31015.html

文章源自JAVA秀-https://www.javaxiu.com/31015.html

来源:jianshu.com/p/09860b74658e文章源自JAVA秀-https://www.javaxiu.com/31015.html

  • 思路文章源自JAVA秀-https://www.javaxiu.com/31015.html

  • Code文章源自JAVA秀-https://www.javaxiu.com/31015.html

  • 多线程测试Spring Boot 使用 AOP 防止重复提交文章源自JAVA秀-https://www.javaxiu.com/31015.html

在传统的web项目中,防止重复提交,通常做法是:后端生成一个唯一的提交令牌(uuid),并存储在服务端。页面提交请求携带这个提交令牌,后端验证并在第一次验证后删除该令牌,保证提交请求的唯一性。文章源自JAVA秀-https://www.javaxiu.com/31015.html

上述的思路其实没有问题的,但是需要前后端都稍加改动,如果在业务开发完在加这个的话,改动量未免有些大了,本节的实现方案无需前端配合,纯后端处理。文章源自JAVA秀-https://www.javaxiu.com/31015.html

思路

  1. 自定义注解 @NoRepeatSubmit 标记所有Controller中的提交请求文章源自JAVA秀-https://www.javaxiu.com/31015.html

  2. 通过AOP 对所有标记了 @NoRepeatSubmit 的方法拦截文章源自JAVA秀-https://www.javaxiu.com/31015.html

  3. 在业务方法执行前,获取当前用户的 token(或者JSessionId)+ 当前请求地址,作为一个唯一 KEY,去获取 Redis 分布式锁(如果此时并发获取,只有一个线程会成功获取锁)文章源自JAVA秀-https://www.javaxiu.com/31015.html

  4. 业务方法执行后,释放锁文章源自JAVA秀-https://www.javaxiu.com/31015.html

关于Redis 分布式锁文章源自JAVA秀-https://www.javaxiu.com/31015.html

  • 不了解的同学戳这里 ==> Redis分布式锁的正确实现方式文章源自JAVA秀-https://www.javaxiu.com/31015.html

  • 使用Redis 是为了在负载均衡部署,如果是单机的部署的项目可以使用一个线程安全的本地Cache 替代 Redis文章源自JAVA秀-https://www.javaxiu.com/31015.html

Code

这里只贴出 AOP 类和测试类,完整代码见 ==> Gitee文章源自JAVA秀-https://www.javaxiu.com/31015.html

@Aspect@Componentpublic class RepeatSubmitAspect {    private final static Logger LOGGER = LoggerFactory.getLogger(RepeatSubmitAspect.class);    @Autowired    private RedisLock redisLock;    @Pointcut("@annotation(noRepeatSubmit)")    public void pointCut(NoRepeatSubmit noRepeatSubmit) {    }    @Around("pointCut(noRepeatSubmit)")    public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) throws Throwable {        int lockSeconds = noRepeatSubmit.lockTime();        HttpServletRequest request = RequestUtils.getRequest();        Assert.notNull(request, "request can not null");        // 此处可以用token或者JSessionId        String token = request.getHeader("Authorization");        String path = request.getServletPath();        String key = getKey(token, path);        String clientId = getClientId();        boolean isSuccess = redisLock.tryLock(key, clientId, lockSeconds);        if (isSuccess) {            LOGGER.info("tryLock success, key = [{}], clientId = [{}]", key, clientId);            // 获取锁成功, 执行进程            Object result;            try {                result = pjp.proceed();            } finally {                // 解锁                redisLock.releaseLock(key, clientId);                LOGGER.info("releaseLock success, key = [{}], clientId = [{}]", key, clientId);            }            return result;        } else {            // 获取锁失败,认为是重复提交的请求            LOGGER.info("tryLock fail, key = [{}]", key);            return new ResultBean(ResultBean.FAIL, "重复请求,请稍后再试", null);        }    }    private String getKey(String token, String path) {        return token + path;    }    private String getClientId() {        return UUID.randomUUID().toString();    }}

多线程测试

测试代码如下,模拟十个请求并发同时提交文章源自JAVA秀-https://www.javaxiu.com/31015.html

@Componentpublic class RunTest implements ApplicationRunner {    private static final Logger LOGGER = LoggerFactory.getLogger(RunTest.class);    @Autowired    private RestTemplate restTemplate;    @Override    public void run(ApplicationArguments args) throws Exception {        System.out.println("执行多线程测试");        String url="http://localhost:8000/submit";        CountDownLatch countDownLatch = new CountDownLatch(1);        ExecutorService executorService = Executors.newFixedThreadPool(10);        for(int i=0; i<10; i++){            String userId = "userId" + i;            HttpEntity request = buildRequest(userId);            executorService.submit(() -> {                try {                    countDownLatch.await();                    System.out.println("Thread:"+Thread.currentThread().getName()+", time:"+System.currentTimeMillis());                    ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);                    System.out.println("Thread:"+Thread.currentThread().getName() + "," + response.getBody());                } catch (InterruptedException e) {                    e.printStackTrace();                }            });        }        countDownLatch.countDown();    }    private HttpEntity buildRequest(String userId) {        HttpHeaders headers = new HttpHeaders();        headers.setContentType(MediaType.APPLICATION_JSON);        headers.set("Authorization", "yourToken");        Map<String, Object> body = new HashMap<>();        body.put("userId", userId);        return new HttpEntity<>(body, headers);    }}

成功防止重复提交,控制台日志如下,可以看到十个线程的启动时间几乎同时发起,只有一个请求提交成功了文章源自JAVA秀-https://www.javaxiu.com/31015.html

Spring Boot 使用 AOP 防止重复提交文章源自JAVA秀-https://www.javaxiu.com/31015.html

文章源自JAVA秀-https://www.javaxiu.com/31015.html

欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢文章源自JAVA秀-https://www.javaxiu.com/31015.html

Spring Boot 使用 AOP 防止重复提交文章源自JAVA秀-https://www.javaxiu.com/31015.html

已在知识星球更新源码解析如下:文章源自JAVA秀-https://www.javaxiu.com/31015.html

Spring Boot 使用 AOP 防止重复提交文章源自JAVA秀-https://www.javaxiu.com/31015.html

Spring Boot 使用 AOP 防止重复提交文章源自JAVA秀-https://www.javaxiu.com/31015.html

Spring Boot 使用 AOP 防止重复提交文章源自JAVA秀-https://www.javaxiu.com/31015.html

Spring Boot 使用 AOP 防止重复提交文章源自JAVA秀-https://www.javaxiu.com/31015.html

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 20 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。文章源自JAVA秀-https://www.javaxiu.com/31015.html

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。文章源自JAVA秀-https://www.javaxiu.com/31015.html

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。文章源自JAVA秀-https://www.javaxiu.com/31015.html

文章源自JAVA秀-https://www.javaxiu.com/31015.html

文章有帮助的话,在看,转发吧。谢谢支持哟 (*^__^*)
文章源自JAVA秀-https://www.javaxiu.com/31015.html

阅读原文文章源自JAVA秀-https://www.javaxiu.com/31015.html

继续阅读
速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
weinxin
资源分享QQ群
本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定