当前位置:   article > 正文

springboot防止重复提交自定义注解_springboot自定义注解防止表单重复提交

springboot自定义注解防止表单重复提交

先看效果:

相关代码 

 配置redis

  1. server:
  2. port: 8282
  3. spring:
  4. # redis 配置
  5. redis:
  6. # 地址
  7. host: 192.168.1.7
  8. # 密码
  9. password: 123456
  10. # 端口,默认为6379
  11. port: 6379
  12. # 数据库索引
  13. database: 0
  14. # 连接超时时间
  15. timeout: 10s
  16. lettuce:
  17. pool:
  18. # 连接池中的最小空闲连接
  19. min-idle: 0
  20. # 连接池中的最大空闲连接
  21. max-idle: 8
  22. # 连接池的最大数据库连接数
  23. max-active: 8
  24. # #连接池最大阻塞等待时间(使用负值表示没有限制)
  25. max-wait: -1ms

pom文件

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  5. <!-- aop切面 -->
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-aop</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter-data-redis</artifactId>
  13. </dependency>
  14. <dependency>
  15. <groupId>cn.hutool</groupId>
  16. <artifactId>hutool-all</artifactId>
  17. <version>5.8.5</version>
  18. </dependency>
  19. <dependency>
  20. <groupId>org.apache.commons</groupId>
  21. <artifactId>commons-pool2</artifactId>
  22. <version>2.0</version>
  23. </dependency>

具体代码,拿去就可以用

  1. package org.example.repeatsubmit;
  2. import cn.hutool.core.convert.Convert;
  3. /**
  4. * 类型转换工具类
  5. */
  6. public class ConvertUtils extends Convert {
  7. }
  1. package org.example.repeatsubmit;
  2. import lombok.extern.slf4j.Slf4j;
  3. import javax.servlet.ServletRequest;
  4. import java.io.BufferedReader;
  5. import java.io.IOException;
  6. import java.io.InputStream;
  7. import java.io.InputStreamReader;
  8. import java.nio.charset.StandardCharsets;
  9. /**
  10. * 通用http工具封装
  11. */
  12. @Slf4j
  13. public class HttpHelperUtils {
  14. /**
  15. * 获取请求Body
  16. *
  17. * @param request 请求
  18. * @return 目标字符串
  19. */
  20. public static String getBodyString(ServletRequest request) {
  21. try (InputStream inputStream = request.getInputStream();
  22. BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
  23. String line;
  24. StringBuilder sb = new StringBuilder();
  25. while (StringUtils.isNotEmpty(line = reader.readLine())) {
  26. sb.append(line);
  27. }
  28. return sb.toString();
  29. } catch (IOException e) {
  30. log.warn("getBodyString出现异常:!", e);
  31. }
  32. return StringUtils.EMPTY;
  33. }
  34. }

  1. package org.example.repeatsubmit;
  2. public class JsonConvertException extends RuntimeException {
  3. private static final long serialVersionUID = 1L;
  4. protected final String message;
  5. public JsonConvertException(String message) {
  6. this.message = message;
  7. }
  8. public JsonConvertException(String message, Throwable e) {
  9. super(message, e);
  10. this.message = message;
  11. }
  12. @Override
  13. public String getMessage() {
  14. return message;
  15. }
  16. }
  1. package org.example.repeatsubmit;
  2. import cn.hutool.json.JSONUtil;
  3. import com.fasterxml.jackson.core.JsonProcessingException;
  4. import com.fasterxml.jackson.core.type.TypeReference;
  5. import com.fasterxml.jackson.databind.ObjectMapper;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.example.rate.SpringUtils;
  8. import java.io.IOException;
  9. @Slf4j
  10. public class JSONUtils extends JSONUtil {
  11. private final static ObjectMapper OBJECT_MAPPER;
  12. static {
  13. OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class);
  14. }
  15. /**
  16. * jackson
  17. * <p>
  18. * 传入对象直接返回Json
  19. *
  20. * @param object 待转换对象
  21. * @return json字符串
  22. */
  23. public static <T> String serialize(T object) {
  24. try {
  25. return OBJECT_MAPPER.writeValueAsString(object);
  26. } catch (JsonProcessingException e) {
  27. log.error("JSON序列化异常:", e);
  28. throw new JsonConvertException("JSON序列化异常!");
  29. }
  30. }
  31. /**
  32. * jackson
  33. * <p>
  34. * 传入Json字符串直接返回bean对象
  35. *
  36. * @param json 待转换Json字符
  37. * @param clazz 待转换对象类型
  38. * @return bean对象
  39. */
  40. public static <T> T deserialize(String json, Class<T> clazz) {
  41. try {
  42. return OBJECT_MAPPER.readValue(json, clazz);
  43. } catch (JsonProcessingException e) {
  44. log.error("JSON反序列化异常:", e);
  45. throw new JsonConvertException("JSON反序列化异常!");
  46. } catch (IOException e) {
  47. e.printStackTrace();
  48. throw new JsonConvertException("JSON反序列化异常!");
  49. }
  50. }
  51. /**
  52. * jackson
  53. * json转bean或list
  54. *
  55. * @param str 带转换字符串
  56. * @param typeReference 转换类型
  57. * @return T 转换后对象
  58. */
  59. public static <T> T getTypeReference(String str, TypeReference<T> typeReference) {
  60. try {
  61. return OBJECT_MAPPER.readValue(str, typeReference);
  62. } catch (JsonProcessingException e) {
  63. log.error("JSON反序列化异常:", e);
  64. throw new JsonConvertException("JSON反序列化异常!");
  65. } catch (IOException e) {
  66. e.printStackTrace();
  67. throw new JsonConvertException("JSON反序列化异常!");
  68. }
  69. }
  70. }

  1. package org.example.repeatsubmit;
  2. import cn.hutool.core.map.MapUtil;
  3. public class MapUtils extends MapUtil {
  4. }
  1. package org.example.repeatsubmit;
  2. import cn.hutool.core.lang.TypeReference;
  3. import org.example.rate.ConvertUtils;
  4. import org.example.rate.SpringUtils;
  5. import org.springframework.data.redis.core.RedisTemplate;
  6. import org.springframework.data.redis.core.ValueOperations;
  7. import org.springframework.data.redis.core.script.RedisScript;
  8. import java.util.List;
  9. import java.util.concurrent.TimeUnit;
  10. public class RedisUtils {
  11. /**
  12. * redisTemplate名称
  13. */
  14. public static final String REDIS_TEMPLATE_NAME = "redisTemplate";
  15. private final static RedisTemplate<Object, Object> redisTemplate;
  16. static {
  17. redisTemplate = org.example.rate.ConvertUtils.convert(new TypeReference<RedisTemplate<Object, Object>>() {},
  18. SpringUtils.getBean(REDIS_TEMPLATE_NAME, RedisTemplate.class));
  19. }
  20. /**
  21. * 缓存基本的对象,Integer、String、实体类等
  22. *
  23. * @param key 缓存的键值
  24. * @param value 缓存的值
  25. */
  26. public static <T> void setCacheObject(final String key, final T value) {
  27. redisTemplate.opsForValue().set(key, value);
  28. }
  29. /**
  30. * 缓存基本的对象,Integer、String、实体类等
  31. *
  32. * @param key 缓存的键值
  33. * @param value 缓存的值
  34. * @param timeout 时间
  35. * @param timeUnit 时间颗粒度
  36. */
  37. public static <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
  38. redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
  39. }
  40. /**
  41. * 获得缓存的基本对象。
  42. *
  43. * @param key 缓存键值
  44. * @return 缓存键值对应的数据
  45. */
  46. public static <T> T getCacheObject(final String key) {
  47. ValueOperations<String, T> operation = ConvertUtils.convert(new TypeReference<ValueOperations<String, T>>() {},
  48. redisTemplate.opsForValue());
  49. return operation.get(key);
  50. }
  51. /**
  52. * 删除单个对象
  53. *
  54. * @param key 缓存键值
  55. */
  56. public static boolean deleteObject(final String key) {
  57. return Boolean.TRUE.equals(redisTemplate.delete(key));
  58. }
  59. /**
  60. * redis 原生脚本执行
  61. *
  62. * @param script RedisScript脚本
  63. * @param keys 缓存键值
  64. * @param args 参数
  65. */
  66. public static <T> T execute(RedisScript<T> script, List<Object> keys, Object... args) {
  67. return redisTemplate.execute(script, keys, args);
  68. }
  69. }
  1. package org.example.repeatsubmit;
  2. import javax.servlet.ReadListener;
  3. import javax.servlet.ServletInputStream;
  4. import javax.servlet.ServletResponse;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletRequestWrapper;
  7. import java.io.BufferedReader;
  8. import java.io.ByteArrayInputStream;
  9. import java.io.IOException;
  10. import java.io.InputStreamReader;
  11. import java.nio.charset.StandardCharsets;
  12. /**
  13. * 构建可重复读取inputStream的request
  14. *
  15. */
  16. public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
  17. private final byte[] body;
  18. public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException {
  19. super(request);
  20. request.setCharacterEncoding(StandardCharsets.UTF_8.name());
  21. response.setCharacterEncoding(StandardCharsets.UTF_8.name());
  22. body = HttpHelperUtils.getBodyString(request).getBytes(StandardCharsets.UTF_8.name());
  23. }
  24. @Override
  25. public BufferedReader getReader() {
  26. return new BufferedReader(new InputStreamReader(getInputStream()));
  27. }
  28. @Override
  29. public ServletInputStream getInputStream() {
  30. final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
  31. return new ServletInputStream() {
  32. @Override
  33. public int read() {
  34. return byteArrayInputStream.read();
  35. }
  36. @Override
  37. public int available() {
  38. return body.length;
  39. }
  40. @Override
  41. public boolean isFinished() {
  42. return false;
  43. }
  44. @Override
  45. public boolean isReady() {
  46. return false;
  47. }
  48. @Override
  49. public void setReadListener(ReadListener readListener) {
  50. }
  51. };
  52. }
  53. }
  1. package org.example.repeatsubmit;
  2. import java.lang.annotation.*;
  3. /**
  4. * 防止表单重复提交注解
  5. */
  6. @Inherited
  7. @Documented
  8. @Target(ElementType.METHOD)
  9. @Retention(RetentionPolicy.RUNTIME)
  10. public @interface RepeatSubmit {
  11. /**
  12. * 间隔时间(ms),小于此时间视为重复提交
  13. */
  14. int interval() default 5000;
  15. /**
  16. * 提示消息
  17. */
  18. String message() default "不允许重复提交,请稍候再试";
  19. }
  1. package org.example.repeatsubmit;
  2. import org.springframework.lang.NonNull;
  3. import org.springframework.web.method.HandlerMethod;
  4. import org.springframework.web.servlet.HandlerInterceptor;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import java.lang.reflect.Method;
  8. import java.util.Objects;
  9. public abstract class RepeatSubmitInterceptor implements HandlerInterceptor {
  10. @Override
  11. public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
  12. if (handler instanceof HandlerMethod) {
  13. HandlerMethod handlerMethod = (HandlerMethod) handler;
  14. Method method = handlerMethod.getMethod();
  15. RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
  16. if (Objects.nonNull(annotation)) {
  17. if (this.isRepeatSubmit(request, annotation)) {
  18. Response<String> result = Response.fail(ResponseCode.FAIL, annotation.message());
  19. ServletUtils.responseJson(response, result);
  20. return false;
  21. }
  22. }
  23. }
  24. return true;
  25. }
  26. /**
  27. * 验证是否重复提交由子类实现具体的防重复提交的规则
  28. *
  29. * @param request 请求
  30. * @param annotation 重复提交注解
  31. * @return 是否是重复提交
  32. */
  33. public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation);
  34. }
  1. package org.example.repeatsubmit;
  2. import java.io.Serializable;
  3. public class Response<T> implements Serializable {
  4. private Integer code;
  5. private String message;
  6. private T data;
  7. private Response() {
  8. }
  9. public Integer getCode() {
  10. return code;
  11. }
  12. public void setCode(Integer code) {
  13. this.code = code;
  14. }
  15. public String getMessage() {
  16. return message;
  17. }
  18. public void setMessage(String message) {
  19. this.message = message;
  20. }
  21. public T getData() {
  22. return data;
  23. }
  24. public void setData(T data) {
  25. this.data = data;
  26. }
  27. public static <T> Response<T> ok() {
  28. return createResult(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getMessage(), null);
  29. }
  30. public static <T> Response<T> ok(T data) {
  31. return createResult(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getMessage(), data);
  32. }
  33. public static <T> Response<T> ok(String message) {
  34. return createResult(ResponseCode.SUCCESS.getCode(), message, null);
  35. }
  36. public static <T> Response<T> ok(T data, String message) {
  37. return createResult(ResponseCode.SUCCESS.getCode(), message, data);
  38. }
  39. public static <T> Response<T> fail() {
  40. return createResult(ResponseCode.FAIL.getCode(), ResponseCode.FAIL.getMessage(), null);
  41. }
  42. public static <T> Response<T> fail(String message) {
  43. return createResult(null, message, null);
  44. }
  45. public static <T> Response<T> fail(Integer code, String message) {
  46. return createResult(code, message, null);
  47. }
  48. public static <T> Response<T> fail(ResponseCode responseCode) {
  49. return createResult(responseCode.getCode(), responseCode.getMessage(), null);
  50. }
  51. public static <T> Response<T> fail(ResponseCode responseCode, T data) {
  52. return createResult(responseCode.getCode(), responseCode.getMessage(), data);
  53. }
  54. public static <T> Response<T> fail(ResponseCode responseCode, String message) {
  55. return createResult(responseCode.getCode(), String.format("%s,%s", responseCode.getMessage(), message), null);
  56. }
  57. private static <T> Response<T> createResult(Integer code, String message, T data) {
  58. Response<T> response = new Response<>();
  59. response.setCode(code);
  60. response.setMessage(message);
  61. response.setData(data);
  62. return response;
  63. }
  64. }
  1. package org.example.repeatsubmit;
  2. public enum ResponseCode {
  3. /**
  4. * 成功
  5. */
  6. SUCCESS(200, "成功"),
  7. /**
  8. * 失败
  9. */
  10. FAIL(201, "失败"),
  11. /**
  12. * 未授权
  13. */
  14. UNAUTHORIZED(202, "未授权"),
  15. /**
  16. * 服务器运行异常
  17. */
  18. RUN_TIME_EXCEPTION(500, "服务器运行异常");
  19. ResponseCode(Integer code, String message) {
  20. this.code = code;
  21. this.message = message;
  22. }
  23. /**
  24. * code
  25. */
  26. final Integer code;
  27. /**
  28. * message desc
  29. */
  30. final String message;
  31. public Integer getCode() {
  32. return code;
  33. }
  34. public String getMessage() {
  35. return message;
  36. }
  37. }
  1. package org.example.repeatsubmit;
  2. import cn.hutool.core.lang.TypeReference;
  3. import lombok.RequiredArgsConstructor;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.web.context.request.NativeWebRequest;
  7. import org.springframework.web.context.request.RequestAttributes;
  8. import org.springframework.web.context.request.ServletWebRequest;
  9. import org.springframework.web.servlet.HandlerMapping;
  10. import javax.servlet.http.HttpServletRequest;
  11. import java.util.HashMap;
  12. import java.util.Map;
  13. import java.util.Objects;
  14. import java.util.concurrent.TimeUnit;
  15. /**
  16. * 判断请求url和数据是否和上一次相同,
  17. * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。
  18. */
  19. @Component
  20. @RequiredArgsConstructor(onConstructor_ = {@Autowired})
  21. public class SameUrlDataInterceptor extends RepeatSubmitInterceptor {
  22. /**
  23. * 防重提交 redis key
  24. */
  25. public static final String REPEAT_SUBMIT_KEY = "repeat_submit:";
  26. /**
  27. * 重复提交参数key
  28. */
  29. public static final String REPEAT_PARAMS = "repeatParams";
  30. /**
  31. * 重复提交时间key
  32. */
  33. public static final String REPEAT_TIME = "repeatTime";
  34. @Override
  35. public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) {
  36. String nowParams = StringUtils.EMPTY;
  37. if (request instanceof RepeatedlyRequestWrapper) {
  38. RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;
  39. nowParams = HttpHelperUtils.getBodyString(repeatedlyRequest);
  40. }
  41. // body参数为空,获取Parameter的数据
  42. if (StringUtils.isEmpty(nowParams)) {
  43. if (MapUtils.isNotEmpty(request.getParameterMap())) {
  44. nowParams = JSONUtils.serialize(request.getParameterMap());
  45. }
  46. }
  47. // Parameter参数为空,获取@PathVariable的数据
  48. if (StringUtils.isEmpty(nowParams)) {
  49. NativeWebRequest webRequest = new ServletWebRequest(request);
  50. Map<String, Object> mapParams = ConvertUtils.convert(new TypeReference<Map<String, Object>>() {},
  51. webRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST));
  52. if (MapUtils.isNotEmpty(mapParams)) {
  53. nowParams = JSONUtils.serialize(mapParams);
  54. }
  55. }
  56. Map<String, Object> nowDataMap = new HashMap<>(2);
  57. nowDataMap.put(REPEAT_PARAMS, nowParams);
  58. nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());
  59. // 请求地址(作为存放cache的key值)
  60. String url = request.getRequestURI();
  61. // 唯一值(没有消息头则使用请求地址)--------系统可以自定义,目前这里定义:Authorization
  62. String submitKey = StringUtils.trimToEmpty(request.getHeader("Authorization"));
  63. // 唯一标识(指定key + url + 消息头)
  64. String cacheRepeatKey = REPEAT_SUBMIT_KEY + url + submitKey;
  65. Object sessionObj = RedisUtils.getCacheObject(cacheRepeatKey);
  66. if (Objects.nonNull(sessionObj)) {
  67. Map<String, Object> sessionMap = ConvertUtils.convert(new TypeReference<Map<String, Object>>() {}, sessionObj);
  68. if (sessionMap.containsKey(url)) {
  69. Map<String, Object> preDataMap = ConvertUtils.convert(new TypeReference<Map<String, Object>>() {}, sessionMap.get(url));
  70. if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) {
  71. return true;
  72. }
  73. }
  74. }
  75. Map<String, Object> cacheMap = new HashMap<>(1);
  76. cacheMap.put(url, nowDataMap);
  77. RedisUtils.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS);
  78. return false;
  79. }
  80. /**
  81. * 判断参数是否相同
  82. */
  83. private boolean compareParams(Map<String, Object> nowMap, Map<String, Object> preMap) {
  84. String nowParams = (String) nowMap.get(REPEAT_PARAMS);
  85. String preParams = (String) preMap.get(REPEAT_PARAMS);
  86. return nowParams.equals(preParams);
  87. }
  88. /**
  89. * 判断两次间隔时间
  90. */
  91. private boolean compareTime(Map<String, Object> nowMap, Map<String, Object> preMap, int interval) {
  92. long time1 = (Long) nowMap.get(REPEAT_TIME);
  93. long time2 = (Long) preMap.get(REPEAT_TIME);
  94. return (time1 - time2) < interval;
  95. }
  96. }
  1. package org.example.repeatsubmit;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.http.MediaType;
  4. import org.springframework.web.context.request.RequestAttributes;
  5. import org.springframework.web.context.request.RequestContextHolder;
  6. import org.springframework.web.context.request.ServletRequestAttributes;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import javax.servlet.http.HttpSession;
  10. import java.io.PrintWriter;
  11. import java.io.UnsupportedEncodingException;
  12. import java.net.URLDecoder;
  13. import java.net.URLEncoder;
  14. import java.nio.charset.StandardCharsets;
  15. import java.util.Objects;
  16. /**
  17. * 客户端响应工具类
  18. */
  19. @Slf4j
  20. public class ServletUtils {
  21. /**
  22. * 获取String参数
  23. */
  24. public static String getParameter(String name) {
  25. return getRequest().getParameter(name);
  26. }
  27. /**
  28. * 获取String参数
  29. */
  30. public static String getHeader(String name) {
  31. return getRequest().getHeader(name);
  32. }
  33. /**
  34. * 获取String参数
  35. */
  36. public static String getParameter(String name, String defaultValue) {
  37. return ConvertUtils.toStr(getRequest().getParameter(name), defaultValue);
  38. }
  39. /**
  40. * 获取Integer参数
  41. */
  42. public static Integer getParameterToInt(String name) {
  43. return ConvertUtils.toInt(getRequest().getParameter(name));
  44. }
  45. /**
  46. * 获取Integer参数
  47. */
  48. public static Integer getParameterToInt(String name, Integer defaultValue) {
  49. return ConvertUtils.toInt(getRequest().getParameter(name), defaultValue);
  50. }
  51. /**
  52. * 获取Boolean参数
  53. */
  54. public static Boolean getParameterToBool(String name) {
  55. return ConvertUtils.toBool(getRequest().getParameter(name));
  56. }
  57. /**
  58. * 获取Boolean参数
  59. */
  60. public static Boolean getParameterToBool(String name, Boolean defaultValue) {
  61. return ConvertUtils.toBool(getRequest().getParameter(name), defaultValue);
  62. }
  63. /**
  64. * 获取request
  65. */
  66. public static HttpServletRequest getRequest() {
  67. return getRequestAttributes().getRequest();
  68. }
  69. /**
  70. * 获取response
  71. */
  72. public static HttpServletResponse getResponse() {
  73. return getRequestAttributes().getResponse();
  74. }
  75. /**
  76. * 获取session
  77. */
  78. public static HttpSession getSession() {
  79. return getRequest().getSession();
  80. }
  81. public static ServletRequestAttributes getRequestAttributes() {
  82. RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
  83. return (ServletRequestAttributes) attributes;
  84. }
  85. /**
  86. * 将字符串渲染到客户端
  87. *
  88. * @param response 渲染对象
  89. * @param failed 响应消息
  90. */
  91. public static void responseJson(HttpServletResponse response, Response<String> failed) {
  92. if (Objects.isNull(failed)) {
  93. return;
  94. }
  95. response.setCharacterEncoding(StandardCharsets.UTF_8.name());
  96. response.setContentType(MediaType.APPLICATION_JSON_VALUE);
  97. try (PrintWriter writer = response.getWriter()) {
  98. String json = JSONUtils.serialize(failed);
  99. writer.write(json);
  100. writer.flush();
  101. } catch (Exception e) {
  102. log.info("响应失败:", e);
  103. }
  104. }
  105. /**
  106. * 内容编码
  107. *
  108. * @param str 内容
  109. * @return 编码后的内容
  110. */
  111. public static String urlEncode(String str) {
  112. try {
  113. return URLEncoder.encode(str, StandardCharsets.UTF_8.name());
  114. } catch (UnsupportedEncodingException e) {
  115. log.info("内容编码失败:", e);
  116. return StringUtils.EMPTY;
  117. }
  118. }
  119. /**
  120. * 内容解码
  121. *
  122. * @param str 内容
  123. * @return 解码后的内容
  124. */
  125. public static String urlDecode(String str) {
  126. try {
  127. return URLDecoder.decode(str, StandardCharsets.UTF_8.name());
  128. } catch (UnsupportedEncodingException e) {
  129. log.info("内容解码失败:", e);
  130. return StringUtils.EMPTY;
  131. }
  132. }
  133. }
  1. package org.example.repeatsubmit;
  2. import cn.hutool.core.text.StrFormatter;
  3. import org.springframework.util.AntPathMatcher;
  4. import java.util.Collection;
  5. import java.util.List;
  6. import java.util.Objects;
  7. public class StringUtils extends org.apache.commons.lang3.StringUtils {
  8. /**
  9. * 斜杠
  10. */
  11. public static final String SLASH = "/";
  12. /**
  13. * 点
  14. */
  15. public static final String SPOT = ".";
  16. /**
  17. * 逗号
  18. */
  19. public static final String COMMA = ",";
  20. /**
  21. * 星号
  22. */
  23. public static final String ASTERISK = "*";
  24. /**
  25. * 与符号
  26. */
  27. public static final String AMPERSAND = "&";
  28. /**
  29. * 等号
  30. */
  31. public static final String EQUAL = "=";
  32. /**
  33. * 横杠
  34. */
  35. public static final String TRANSVERSE = "-";
  36. /**
  37. * 下划线
  38. */
  39. public static final String SEPARATOR = "_";
  40. /**
  41. * 空格
  42. */
  43. public static final String SPACE = " ";
  44. /**
  45. * 冒号
  46. */
  47. public static final String COLON = ":";
  48. /**
  49. * * 判断一个对象数组是否为空
  50. *
  51. * @param objects 要判断的对象数组
  52. * * @return true:为空 false:非空
  53. */
  54. public static boolean isEmpty(Object[] objects) {
  55. return isNull(objects) || (objects.length == 0);
  56. }
  57. /**
  58. * * 判断一个对象是否为空
  59. *
  60. * @param object Object
  61. * @return true:为空 false:非空
  62. */
  63. public static boolean isNull(Object object) {
  64. return object == null;
  65. }
  66. /**
  67. * * 判断一个Collection是否为空, 包含List,Set,Queue
  68. *
  69. * @param coll 要判断的Collection
  70. * @return true:为空 false:非空
  71. */
  72. public static boolean isEmpty(Collection<?> coll) {
  73. return Objects.isNull(coll) || coll.isEmpty();
  74. }
  75. /**
  76. * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
  77. *
  78. * @param str 指定字符串
  79. * @param characters 需要检查的字符串数组
  80. * @return 是否匹配
  81. */
  82. public static boolean matches(String str, List<String> characters) {
  83. if (isEmpty(str) || isEmpty(characters)) {
  84. return false;
  85. }
  86. for (String pattern : characters) {
  87. if (isMatch(pattern, str)) {
  88. return true;
  89. }
  90. }
  91. return false;
  92. }
  93. /**
  94. * 判断url是否与规则配置:
  95. * ? 表示单个字符;
  96. * * 表示一层路径内的任意字符串,不可跨层级;
  97. * ** 表示任意层路径;
  98. *
  99. * @param pattern 匹配规则
  100. * @param url 需要匹配的url
  101. * @return 是否与规则配置结果
  102. */
  103. public static boolean isMatch(String pattern, String url) {
  104. AntPathMatcher matcher = new AntPathMatcher();
  105. return matcher.match(pattern, url);
  106. }
  107. /**
  108. * 格式化文本, {} 表示占位符<br>
  109. * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
  110. * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
  111. * 例:<br>
  112. * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
  113. * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
  114. * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
  115. *
  116. * @param template 文本模板,被替换的部分用 {} 表示
  117. * @param params 参数值
  118. * @return 格式化后的文本
  119. */
  120. public static String format(String template, Object... params) {
  121. if (isEmpty(params) || isEmpty(template)) {
  122. return template;
  123. }
  124. return StrFormatter.format(template, params);
  125. }
  126. /**
  127. * 驼峰转下划线命名
  128. */
  129. public static String toUnderScoreCase(String str) {
  130. if (Objects.isNull(str)) {
  131. return null;
  132. }
  133. StringBuilder sb = new StringBuilder();
  134. // 前置字符是否大写
  135. boolean preCharIsUpperCase;
  136. // 当前字符是否大写
  137. boolean currentCharIsUpperCase;
  138. // 下一字符是否大写
  139. boolean nextCharIsUpperCase = true;
  140. for (int i = 0; i < str.length(); i++) {
  141. char c = str.charAt(i);
  142. if (i > 0) {
  143. preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
  144. } else {
  145. preCharIsUpperCase = false;
  146. }
  147. currentCharIsUpperCase = Character.isUpperCase(c);
  148. if (i < (str.length() - 1)) {
  149. nextCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
  150. }
  151. if (preCharIsUpperCase && currentCharIsUpperCase && !nextCharIsUpperCase) {
  152. sb.append(SEPARATOR);
  153. } else if ((i != 0 && !preCharIsUpperCase) && currentCharIsUpperCase) {
  154. sb.append(SEPARATOR);
  155. }
  156. sb.append(Character.toLowerCase(c));
  157. }
  158. return sb.toString();
  159. }
  160. }
  1. package org.example.rate;
  2. import org.springframework.aop.framework.AopContext;
  3. import org.springframework.beans.BeansException;
  4. import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
  5. import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
  6. import org.springframework.context.ApplicationContext;
  7. import org.springframework.context.ApplicationContextAware;
  8. import org.springframework.lang.NonNull;
  9. import org.springframework.stereotype.Component;
  10. /**
  11. * spring 工具类
  12. */
  13. @Component
  14. public class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware {
  15. /**
  16. * Spring应用上下文环境
  17. */
  18. private static ConfigurableListableBeanFactory beanFactory;
  19. private static ApplicationContext applicationContext;
  20. @Override
  21. public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
  22. SpringUtils.beanFactory = beanFactory;
  23. }
  24. /**
  25. * private static ConfigurableListableBeanFactory beanFactory;
  26. * SpringUtils.applicationContext = applicationContext;
  27. */
  28. @Override
  29. public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
  30. SpringUtils.applicationContext = applicationContext;
  31. }
  32. /**
  33. * 获取对象
  34. *
  35. * @param name bean名称
  36. * @param clazz 待获取类型
  37. * @return Object 一个以所给名字注册的bean的实例
  38. */
  39. public static <T> T getBean(String name, Class<T> clazz) {
  40. return beanFactory.getBean(name, clazz);
  41. }
  42. /**
  43. * 获取类型为requiredType的对象
  44. *
  45. * @param clz 类型
  46. * @return Object 一个以所给类型注册的bean的实例
  47. */
  48. public static <T> T getBean(Class<T> clz) {
  49. return beanFactory.getBean(clz);
  50. }
  51. /**
  52. * 发布一个事件
  53. *
  54. * @param event 事件
  55. */
  56. public static void publishEvent(Object event) {
  57. applicationContext.publishEvent(event);
  58. }
  59. /**
  60. * 获取当前代理bean
  61. * @param t 待获取bean类型
  62. * @param <T> 泛型
  63. * @return 当前代理bean
  64. */
  65. public static <T> T getCurrentProxyBean(Class<T> t) {
  66. return ConvertUtils.convert(t, AopContext.currentProxy());
  67. }
  68. }

最后写springmvc拦截

  1. package org.example.repeatsubmit;
  2. import lombok.RequiredArgsConstructor;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.http.MediaType;
  6. import org.springframework.http.converter.HttpMessageConverter;
  7. import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
  8. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  9. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  10. import java.nio.charset.StandardCharsets;
  11. import java.util.ArrayList;
  12. import java.util.List;
  13. @Configuration
  14. @RequiredArgsConstructor(onConstructor_ = {@Autowired})
  15. public class WebMvcConfig implements WebMvcConfigurer {
  16. private final RepeatSubmitInterceptor repeatSubmitInterceptor;
  17. /**
  18. * 解决低版本ie @ResponseBody返回json的时候提示下载问题 Jackson处理
  19. */
  20. public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter() {
  21. MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
  22. List<MediaType> supportedMediaTypes = new ArrayList<>();
  23. MediaType media = new MediaType(MediaType.TEXT_PLAIN, StandardCharsets.UTF_8);
  24. supportedMediaTypes.add(media);
  25. jsonConverter.setSupportedMediaTypes(supportedMediaTypes);
  26. return jsonConverter;
  27. }
  28. @Override
  29. public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  30. converters.add(customJackson2HttpMessageConverter());
  31. }
  32. /**
  33. * 自定义拦截规则
  34. */
  35. @Override
  36. public void addInterceptors(InterceptorRegistry registry) {
  37. registry.addInterceptor(repeatSubmitInterceptor)
  38. .addPathPatterns("/**");
  39. }
  40. }

测试代码

  1. @RepeatSubmit(interval = 1000)
  2. @PostMapping("/repeatSubmit")
  3. public String repeatSubmit(@RequestParam String test){
  4. log.info("repeatSubmit--test--->"+test);
  5. return "ok";
  6. }

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

闽ICP备14008679号