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()); } @BeanRedisTemplate getRedisTemplate(){ RedisTemplate redisTemplate=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){ ValueOperations opsForValue=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("程序逻辑返回"); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。