Spring Boot如何防止重复提交
场景:同一个用户在2秒内对同一URL的提交视为重复提交。
思考逻辑:
1.从数据库方面考虑,数据设计的时候,某些数据有没有唯一性,如果有唯一性,要考虑设置唯一索引,可以避免脏数据。
2.从应用层面考虑,首先判断是单机服务还是分布式服务,则此时需要考虑一些缓存,利用缓存,来保证数据的重复提交。
假设是分布式应用,则可以将用户的信息,例如token和请求的url进行组装在一起,存储到缓存存,例如redis,并设置超时时间为2秒,如此来保证数据的唯一性。
以下是代码实现:
Application.java
packagecom;
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
/**
*@authorwww.spring.tsh
*@功能描述防重复提交
*@date2018-08-26
*/
@SpringBootApplication
publicclassApplication{
publicstaticvoidmain(String[]args){
SpringApplication.run(Application.class,args);
}
}
application.yml
spring: redis: host:127.0.0.1 port:6379 password:123456
RedisConfig.java
packagecom.common;
importorg.springframework.boot.context.properties.ConfigurationProperties;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.data.redis.connection.RedisStandaloneConfiguration;
importorg.springframework.data.redis.connection.jedis.JedisClientConfiguration;
importorg.springframework.data.redis.connection.jedis.JedisConnectionFactory;
importorg.springframework.data.redis.core.RedisTemplate;
@Configuration
publicclassRedisConfig{
@Bean
@ConfigurationProperties(prefix="spring.redis")
publicJedisConnectionFactorygetConnectionFactory(){
returnnewJedisConnectionFactory(newRedisStandaloneConfiguration(),JedisClientConfiguration.builder().build());
}
@Bean
RedisTemplategetRedisTemplate(){
RedisTemplateredisTemplate=newRedisTemplate();
redisTemplate.setConnectionFactory(getConnectionFactory());
returnredisTemplate;
}
}
自定义注解NoRepeatSubmit.java
packagecom.common;
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
@Target(ElementType.METHOD)//作用到方法上
@Retention(RetentionPolicy.RUNTIME)//运行时有效
/**
*@功能描述防止重复提交标记注解
*@authorwww.srping.tsh
*@date2018-08-26
*/
public@interfaceNoRepeatSubmit{
}
aop解析注解NoRepeatSubmitAop.java
packagecom.common;
importjavax.servlet.http.HttpServletRequest;
importorg.apache.commons.logging.Log;
importorg.apache.commons.logging.LogFactory;
importorg.aspectj.lang.ProceedingJoinPoint;
importorg.aspectj.lang.annotation.Around;
importorg.aspectj.lang.annotation.Aspect;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.data.redis.core.RedisTemplate;
importorg.springframework.data.redis.core.ValueOperations;
importorg.springframework.stereotype.Component;
importorg.springframework.web.context.request.RequestContextHolder;
importorg.springframework.web.context.request.ServletRequestAttributes;
@Aspect
@Component
/**
*@功能描述aop解析注解
*@authorwww.gaozz.club
*@date2018-11-02
*/
publicclassNoRepeatSubmitAop{
privateLoglogger=LogFactory.getLog(getClass());
@Autowired
privateRedisTemplatetemplate;
@Around("execution(*com.example..*Controller.*(..))&&@annotation(nrs)")
publicObjectarround(ProceedingJoinPointpjp,NoRepeatSubmitnrs){
ValueOperationsopsForValue=template.opsForValue();
try{
ServletRequestAttributesattributes=(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
StringsessionId=RequestContextHolder.getRequestAttributes().getSessionId();
HttpServletRequestrequest=attributes.getRequest();
Stringkey=sessionId+"-"+request.getServletPath();
if(opsForValue.get(key)==null){//如果缓存中有这个url视为重复提交
Objecto=pjp.proceed();
opsForValue.set(key,0,2,TimeUnit.SECONDS);
returno;
}else{
logger.error("重复提交");
returnnull;
}
}catch(Throwablee){
e.printStackTrace();
logger.error("验证重复提交时出现未知异常!");
return"{\"code\":-889,\"message\":\"验证重复提交时出现未知异常!\"}";
}
}
}
测试类:
packagecom.example;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RestController;
importcom.common.NoRepeatSubmit;
/**
*@功能描述测试Controller
*@authorwww.spring.tsh
*@date2018-08-26
*/
@RestController
publicclassTestController{
@RequestMapping("/test")
@NoRepeatSubmit
publicStringtest(){
return("程序逻辑返回");
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。