当前位置:   article > 正文

spring boot 防止重复提交_验证重复提交时,出现未知异常

验证重复提交时,出现未知异常

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

上代码吧

pom.xml

 

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>com.example</groupId>
  6. <artifactId>springboot-repeat-submit</artifactId>
  7. <version>1.0</version>
  8. <packaging>jar</packaging>
  9. <parent>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-parent</artifactId>
  12. <version>2.0.4.RELEASE</version>
  13. <relativePath/> <!-- lookup parent from repository -->
  14. </parent>
  15. <properties>
  16. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  17. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  18. <java.version>1.8</java.version>
  19. </properties>
  20. <dependencies>
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-starter-web</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-aop</artifactId>
  28. </dependency>
  29. <dependency>
  30. <groupId>com.google.guava</groupId>
  31. <artifactId>guava</artifactId>
  32. <version>24.0-jre</version>
  33. </dependency>
  34. </dependencies>
  35. <build>
  36. <plugins>
  37. <plugin>
  38. <groupId>org.springframework.boot</groupId>
  39. <artifactId>spring-boot-maven-plugin</artifactId>
  40. </plugin>
  41. </plugins>
  42. </build>
  43. </project>

logback.xml

 

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3. <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  4. <encoder>
  5. <pattern>%d[%F:%L][%p]:%m%n</pattern>
  6. </encoder>
  7. </appender>
  8. <root level="info">
  9. <appender-ref ref="STDOUT"/>
  10. </root>
  11. </configuration>

Application.java

 

  1. package com;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. /**
  5. * @author www.gaozz.club
  6. * @功能描述 防重复提交
  7. * @date 2018-08-26
  8. */
  9. @SpringBootApplication
  10. public class Application {
  11. public static void main(String[] args) {
  12. SpringApplication.run(Application.class, args);
  13. }
  14. }

自定义注解NoRepeatSubmit.java

 

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

aop解析注解NoRepeatSubmitAop.java

 

  1. package com.common;
  2. import javax.servlet.http.HttpServletRequest;
  3. import org.apache.commons.logging.Log;
  4. import org.apache.commons.logging.LogFactory;
  5. import org.aspectj.lang.ProceedingJoinPoint;
  6. import org.aspectj.lang.annotation.Around;
  7. import org.aspectj.lang.annotation.Aspect;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.stereotype.Component;
  10. import org.springframework.web.context.request.RequestContextHolder;
  11. import org.springframework.web.context.request.ServletRequestAttributes;
  12. import com.google.common.cache.Cache;
  13. @Aspect
  14. @Component
  15. /**
  16. * @功能描述 aop解析注解
  17. * @author www.gaozz.club
  18. * @date 2018-08-26
  19. */
  20. public class NoRepeatSubmitAop {
  21. private Log logger = LogFactory.getLog(getClass());
  22. @Autowired
  23. private Cache<String, Integer> cache;
  24. @Around("execution(* com.example..*Controller.*(..)) && @annotation(nrs)")
  25. public Object arround(ProceedingJoinPoint pjp, NoRepeatSubmit nrs) {
  26. try {
  27. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  28. String sessionId = RequestContextHolder.getRequestAttributes().getSessionId();
  29. HttpServletRequest request = attributes.getRequest();
  30. String key = sessionId + "-" + request.getServletPath();
  31. if (cache.getIfPresent(key) == null) {// 如果缓存中有这个url视为重复提交
  32. Object o = pjp.proceed();
  33. cache.put(key, 0);
  34. return o;
  35. } else {
  36. logger.error("重复提交");
  37. return null;
  38. }
  39. } catch (Throwable e) {
  40. e.printStackTrace();
  41. logger.error("验证重复提交时出现未知异常!");
  42. return "{\"code\":-889,\"message\":\"验证重复提交时出现未知异常!\"}";
  43. }
  44. }
  45. }

缓存类

 

  1. package com.common;
  2. import java.util.concurrent.TimeUnit;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import com.google.common.cache.Cache;
  6. import com.google.common.cache.CacheBuilder;
  7. @Configuration
  8. /**
  9. * @功能描述 内存缓存
  10. * @author www.gaozz.club
  11. * @date 2018-08-26
  12. */
  13. public class UrlCache {
  14. @Bean
  15. public Cache<String, Integer> getCache() {
  16. return CacheBuilder.newBuilder().expireAfterWrite(2L, TimeUnit.SECONDS).build();// 缓存有效期为2
  17. }
  18. }

测试Controller

 

  1. package com.example;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.RestController;
  4. import com.common.NoRepeatSubmit;
  5. /**
  6. * @功能描述 测试Controller
  7. * @author www.gaozz.club
  8. * @date 2018-08-26
  9. */
  10. @RestController
  11. public class TestController {
  12. @RequestMapping("/test")
  13. @NoRepeatSubmit
  14. public String test() {
  15. return ("程序逻辑返回");
  16. }
  17. }

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

然后F5刷新查看效果

 

QQ图片20180914165017.png


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


application.yml

 

  1. spring:
  2. redis:
  3. host: 192.168.1.92
  4. port: 6379
  5. password: 123456

RedisConfig.java

 

  1. package com.common;
  2. import org.springframework.boot.context.properties.ConfigurationProperties;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
  6. import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
  7. import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
  8. import org.springframework.data.redis.core.RedisTemplate;
  9. @Configuration
  10. public class RedisConfig {
  11. @Bean
  12. @ConfigurationProperties(prefix = "spring.redis")
  13. public JedisConnectionFactory getConnectionFactory() {
  14. return new JedisConnectionFactory(new RedisStandaloneConfiguration(), JedisClientConfiguration.builder().build());
  15. }
  16. @Bean
  17. <K, V> RedisTemplate<K, V> getRedisTemplate() {
  18. RedisTemplate<K, V> redisTemplate = new RedisTemplate<K, V>();
  19. redisTemplate.setConnectionFactory(getConnectionFactory());
  20. return redisTemplate;
  21. }
  22. }

调整切面类NoRepeatSubmitAop.java

 

  1. package com.common;
  2. import javax.servlet.http.HttpServletRequest;
  3. import org.apache.commons.logging.Log;
  4. import org.apache.commons.logging.LogFactory;
  5. import org.aspectj.lang.ProceedingJoinPoint;
  6. import org.aspectj.lang.annotation.Around;
  7. import org.aspectj.lang.annotation.Aspect;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.data.redis.core.RedisTemplate;
  10. import org.springframework.data.redis.core.ValueOperations;
  11. import org.springframework.stereotype.Component;
  12. import org.springframework.web.context.request.RequestContextHolder;
  13. import org.springframework.web.context.request.ServletRequestAttributes;
  14. @Aspect
  15. @Component
  16. /**
  17. * @功能描述 aop解析注解
  18. * @author www.gaozz.club
  19. * @date 2018-11-02
  20. */
  21. public class NoRepeatSubmitAop {
  22. private Log logger = LogFactory.getLog(getClass());
  23. @Autowired
  24. private RedisTemplate<String, Integer> template;
  25. @Around("execution(* com.example..*Controller.*(..)) && @annotation(nrs)")
  26. public Object arround(ProceedingJoinPoint pjp, NoRepeatSubmit nrs) {
  27. ValueOperations<String, Integer> opsForValue = template.opsForValue();
  28. try {
  29. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  30. String sessionId = RequestContextHolder.getRequestAttributes().getSessionId();
  31. HttpServletRequest request = attributes.getRequest();
  32. String key = sessionId + "-" + request.getServletPath();
  33. if (opsForValue.get(key) == null) {// 如果缓存中有这个url视为重复提交
  34. Object o = pjp.proceed();
  35. opsForValue.set(key, 0, 2, TimeUnit.SECONDS);
  36. return o;
  37. } else {
  38. logger.error("重复提交");
  39. return null;
  40. }
  41. } catch (Throwable e) {
  42. e.printStackTrace();
  43. logger.error("验证重复提交时出现未知异常!");
  44. return "{\"code\":-889,\"message\":\"验证重复提交时出现未知异常!\"}";
  45. }
  46. }
  47. }

https://github.com/gzz2017gzz/spring-boot2-example/tree/master/54-spring-boot-repeat-submit-single



作者:不知不怪
链接:https://www.jianshu.com/p/09c6b05b670a
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/234377?site
推荐阅读
相关标签