spring boot 防止重复提交实现方法详解

 更新时间:2019年11月06日 10:49:11   作者:苍青浪  
这篇文章主要介绍了spring boot 防止重复提交实现方法,结合实例形式详细分析了spring boot 防止重复提交具体配置、实现方法及操作注意事项,需要的朋友可以参考下

本文实例讲述了spring boot 防止重复提交实现方法。分享给大家供大家参考,具体如下:

服务器端实现方案:同一客户端在2秒内对同一URL的提交视为重复提交

上代码吧

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.example</groupId>
 <artifactId>springboot-repeat-submit</artifactId>
 <version>1.0</version>
 <packaging>jar</packaging>
 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.0.4.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
 </parent>
 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <java.version>1.8</java.version>
 </properties>
 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
  </dependency>
  <dependency>
   <groupId>com.google.guava</groupId>
   <artifactId>guava</artifactId>
   <version>24.0-jre</version>
  </dependency>
 </dependencies>
 <build>
  <plugins>
   <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
   </plugin>
  </plugins>
 </build>
</project>

Application.java

package com;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * @author www.gaozz.club
 * @功能描述 防重复提交
 * @date 2018-08-26
 */
@SpringBootApplication
public class Application {
 public static void main(String[] args) {
  SpringApplication.run(Application.class, args);
 }
}

自定义注解NoRepeatSubmit.java

package com.common;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // 作用到方法上
@Retention(RetentionPolicy.RUNTIME) // 运行时有效
/**
 * @功能描述 防止重复提交标记注解
 * @author www.gaozz.club
 * @date 2018-08-26
 */
public @interface NoRepeatSubmit {
}

aop解析注解NoRepeatSubmitAop.java

package com.common;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.google.common.cache.Cache;
@Aspect
@Component
/**
 * @功能描述 aop解析注解
 * @author www.gaozz.club
 * @date 2018-08-26
 */
public class NoRepeatSubmitAop {
 private Log logger = LogFactory.getLog(getClass());
 @Autowired
 private Cache<String, Integer> cache;
 @Around("execution(* com.example..*Controller.*(..)) && @annotation(nrs)")
 public Object arround(ProceedingJoinPoint pjp, NoRepeatSubmit nrs) {
  try {
   ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
   String sessionId = RequestContextHolder.getRequestAttributes().getSessionId();
   HttpServletRequest request = attributes.getRequest();
   String key = sessionId + "-" + request.getServletPath();
   if (cache.getIfPresent(key) == null) {// 如果缓存中有这个url视为重复提交
    Object o = pjp.proceed();
    cache.put(key, 0);
    return o;
   } else {
    logger.error("重复提交");
    return null;
   }
  } catch (Throwable e) {
   e.printStackTrace();
   logger.error("验证重复提交时出现未知异常!");
   return "{\"code\":-889,\"message\":\"验证重复提交时出现未知异常!\"}";
  }
 }
}

缓存类

package com.common;
import java.util.concurrent.TimeUnit;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
@Configuration
/**
 * @功能描述 内存缓存
 * @author www.gaozz.club
 * @date 2018-08-26
 */
public class UrlCache {
 @Bean
 public Cache<String, Integer> getCache() {
  return CacheBuilder.newBuilder().expireAfterWrite(2L, TimeUnit.SECONDS).build();// 缓存有效期为2秒
 }
}

测试Controller

package com.example;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.common.NoRepeatSubmit;
/**
 * @功能描述 测试Controller
 * @author www.gaozz.club
 * @date 2018-08-26
 */
@RestController
public class TestController {
 @RequestMapping("/test")
 @NoRepeatSubmit
 public String test() {
  return ("程序逻辑返回");
 }
}

浏览器输入http://localhost:8080/test

然后F5刷新查看效果

以下为新版内容:解决了程序集群部署时请求可能会落到多台机器上的问题,把内存缓存换成了redis

application.yml

spring:
 redis:
 host: 192.168.1.92
 port: 6379
 password: 123456

RedisConfig.java

package com.common;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
public class RedisConfig {
 @Bean
 @ConfigurationProperties(prefix = "spring.redis")
 public JedisConnectionFactory getConnectionFactory() {
  return new JedisConnectionFactory(new RedisStandaloneConfiguration(), JedisClientConfiguration.builder().build());
 }
 @Bean
 <K, V> RedisTemplate<K, V> getRedisTemplate() {
  RedisTemplate<K, V> redisTemplate = new RedisTemplate<K, V>();
  redisTemplate.setConnectionFactory(getConnectionFactory());
  return redisTemplate;
 }
}

调整切面类NoRepeatSubmitAop.java

package com.common;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@Aspect
@Component
/**
 * @功能描述 aop解析注解
 * @author www.gaozz.club
 * @date 2018-11-02
 */
public class NoRepeatSubmitAop {
 private Log logger = LogFactory.getLog(getClass());
 @Autowired
 private RedisTemplate<String, Integer> template;
 @Around("execution(* com.example..*Controller.*(..)) && @annotation(nrs)")
 public Object arround(ProceedingJoinPoint pjp, NoRepeatSubmit nrs) {
  ValueOperations<String, Integer> opsForValue = template.opsForValue();
  try {
   ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
   String sessionId = RequestContextHolder.getRequestAttributes().getSessionId();
   HttpServletRequest request = attributes.getRequest();
   String key = sessionId + "-" + request.getServletPath();
   if (opsForValue.get(key) == null) {// 如果缓存中有这个url视为重复提交
    Object o = pjp.proceed();
    opsForValue.set(key, 0, 2, TimeUnit.SECONDS);
    return o;
   } else {
    logger.error("重复提交");
    return null;
   }
  } catch (Throwable e) {
   e.printStackTrace();
   logger.error("验证重复提交时出现未知异常!");
   return "{\"code\":-889,\"message\":\"验证重复提交时出现未知异常!\"}";
  }
 }
}

附:GitHub源码地址:https://github.com/gzz2017gzz/spring-boot2-example/tree/master/54-spring-boot-repeat-submit-single

更多关于java相关内容感兴趣的读者可查看本站专题:《Spring框架入门与进阶教程》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总

希望本文所述对大家java程序设计有所帮助。

相关文章

  • Java遍历Properties所有元素的方法实例

    Java遍历Properties所有元素的方法实例

    这篇文章主要介绍了Java如何遍历Properties所有元素的方法,大家可以参考使用
    2013-11-11
  • 设计模式之模版方法模式_动力节点Java学院整理

    设计模式之模版方法模式_动力节点Java学院整理

    这篇文章主要介绍了设计模式之模版方法模式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • Kotlin 基础教程之异常

    Kotlin 基础教程之异常

    这篇文章主要介绍了Kotlin 基础教程之异常的相关资料,需要的朋友可以参考下
    2017-06-06
  • @RequestParam注解加与不加有什么区别

    @RequestParam注解加与不加有什么区别

    这篇文章主要介绍了@RequestParam注解加与不加有什么区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • Java中实现多重排序的几种方法小结

    Java中实现多重排序的几种方法小结

    Java中的多重排序通常指的是同时对一个集合中的两个或更多列或多维度的数据进行排序,这通常通过自定义Comparator实现,可以结合Arrays.sort()或Collections.sort()方法,当需要进行多重排序时,即根据多个字段进行排序,我们可以采用以下几种方法
    2024-10-10
  • SpringBoot整合Spring Data JPA的详细方法

    SpringBoot整合Spring Data JPA的详细方法

    JPA全称为Java Persistence API(Java持久层API),是一个基于ORM的标准规范,在这个规范中,JPA只定义标准规则,不提供实现,本文重点给大家介绍SpringBoot整合Spring Data JPA的相关知识,感兴趣的朋友一起看看吧
    2022-02-02
  • Java实现幂等性校验的示例代码

    Java实现幂等性校验的示例代码

    我们在做web应用的时候通常会遇到前端提交按钮重复点击的场景,在某些新增操作上就需要做幂等性限制来保证数据的可靠性,所以本文主要介绍了如何使用java aop实现幂等性校验,需要的可以参考下
    2024-02-02
  • Java实现短信发送验证码功能

    Java实现短信发送验证码功能

    这篇文章主要介绍了Java实现短信发送验证码功能,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2018-10-10
  • Java中使用DOM4J生成xml文件并解析xml文件的操作

    Java中使用DOM4J生成xml文件并解析xml文件的操作

    这篇文章主要介绍了Java中使用DOM4J来生成xml文件和解析xml文件的操作,今天通过代码给大家展示了解析xml文件和生成xml文件的方法,需要的朋友可以参考下
    2021-09-09
  • 深入理解spring的AOP机制原理

    深入理解spring的AOP机制原理

    本篇文章主要介绍了深入理解spring的AOP机制原理,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09

最新评论