springboot2.xAOP实现缓存锁,分布式锁防止重复提交。深耕后台系统多年经验;当用户处于糟糕的网络中时;提交表单时,会出现重复提交;所以我们需要:防止表单重新报告google的guave缓存org.springframework.bootspring-boot-starter-web<依赖关系>org.springframework.bootspring-boot-starter-aopcom.google.guavaguava21.0注解接口包com.ouyue.xiwenapi.annotation;importjava.lang.annotation.*;/***@ClassName:${}*@Description:TODO*@作者:xx@163。com*@Date:*/@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic@interfaceGuaveLock{Stringkey()default"";/***过期时间TODO暂时忽略这个由于使用guava的属性栏集成redis需要使用**@authorfly*/intexpire()default5;}AOP应用包com.ouyue.xiwenapi.config;importcom.google.common.cache.Cache;importcom.google.common.cache.CacheBuilder;importcom.ouyue.xiwenapi.annotation.GuaveLock;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation。周围;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.reflect.MethodSignature;importorg.springframework.context.annotation.Configuration;importorg.springframework.util.StringUtils;importjava.lang.reflect.Method;importjava。util.concurrent.TimeUnit;/***@ClassName:${}*@Description:TODO*@author:xx@163.com*@Date:*/@Aspect@ConfigurationpublicclassLockMethodAopConfigure{privatestaticfinalCacheCACHES=CacheBuilder.newBuilder()//最大保存100个.maximumSize(1000)//设置写入保存后5秒钟过期.expireAfterWrite(5,TimeUnit.SECONDS).build();@Around("execution(public**(..))&&@annotation(com.ouyue.xiwenapi.annotation.GuaveLock)")publicObjectinterceptor(ProceedingJoinPointpjp){MethodSignaturesignature=(MethodSignature)pjp.getSignature();Methodmethod=signature.getMethod();GuaveLocklocalLock=method.getAnnotation(GuaveLock.class);Stringkey=getKey(localLock.key(),pjp.getArgs());if(!StringUtils.isEmpty(key)){if(CACHES.getIfPresent(key)!=null){thrownewRuntimeException("不要重复请求");}//如果是第一次请求,将key当前对象压入缓存CACHES.put(key,key);}try{returnpjp.proceed();}catch(Throwablethrowable){thrownewRuntimeException("Serverexception");}finally{//TODO为了演示效果,这里不调用CACHES.invalidate(key);code}}/***key生成策略,如果想灵活一点,可以写成接口和实现类(TODO后续讲解)**@paramkeyExpress表达式*@paramargs参数可以加密成一个*@return生成的密钥*/privateStringgetKey(StringkeyExpress,Object[]args){for(inti=0;iorg.springframework.bootspring-boot-starter-data-redisspring.redis.host=localhostspring.redis.port=6379RedisLock前缀:key在缓存中的前缀expire:过期时间,这里默认5秒timeUnit:超时单位,这里默认秒delimiter:key的分隔符,分隔不同的参数值packagecom.ouyue.xiwenapi.annotation;importjava.lang.annotation.*;importjava.util.concurrent.TimeUnit;/***@ClassName:${}*@Description:TODO*@author:xx@163.com*@Date:*/@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic@interfaceRedisLock{/***redis锁键前缀**@returnredis锁键前缀*/Stringprefix()default"";/***过期秒数,默认5秒**@return轮询锁定时间*/intexpire()default5;/***超时时间单位**@return秒*/TimeUnittimeUnit()defaultTimeUnit.SECONDS;/***KeyDelimiter(default:)
*GeneratedKey:N:SO1008:500
**@returnString*/Stringdelimiter()default":";}CacheParam注解包com.ouyue.xiwenapi.annotation;importjava.lang.annotation.*;/***@ClassName:${}*@Description:TODO*@author:xx@163.com*@Date:*/@Target({ElementType.PARAMETER,ElementType.METHOD,ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic@interfaceCacheParam{/***字段名称**@returnString*/Stringname()default"";}Key生成策略包com.ouyue.xiwenapi.componet;importorg.aspectj.lang.ProceedingJoinPoint;publicinterfaceCacheKeyGenerator{/***获取AOP参数,生成指定缓存Key**@parampjpPJP*@returncacheKEY*/StringgetLockKey(ProceedingJoinPointpjp);}Key生成策略(实现).componet.CacheKeyGenerator;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.reflect.MethodSignature;importorg.springframework.util.ReflectionUtils;importorg.springframework.util.StringUtils;importjava.lang.annotation.Annotation;importjava.lang.reflect.Field;importjava.lang.reflect.Method;importjava.lang.reflect.Parameter;/***@ClassName:${}*@说明:TODO*@author:xx@163.com*@Date:*/publicclassLockKeyGeneratorimplementsCacheKeyGenerator{@OverridepublicStringgetLockKey(ProceedingJoinPointpjp){MethodSignaturesignature=(MethodSignature)pjp.getSignature();Methodmethod=signature.getAnmethodlock();RedisLockthodlockAn(RedisLock.class);finalObject[]args=pjp.getArgs();finalParameter[]parameters=method.getParameters();StringBuilderbuilder=newStringBuilder();//TODO默认解析方法带CacheParam注解属性,如果不行试试For(inti=0;i)connection->connection.set(lockKey.getBytes(),newbyte[0],Expiration.from(lock.expire(),lock.timeUnit()),RedisStringCommands.SetOption.SET_IF_ABSENT));if(!success){//TODO从逻辑上讲,我们应该抛出一个自定义的CacheLockException;这里是懒惰的thrownewRuntimeException("不要重复请求");}try{returnpjp.proceed();}catch(Throwablethrowable){thrownewRuntimeException("SystemException");}}finally{//TODO如果需要注释代码,如果你想演示它;实际上你应该让它去.xiwenapi.annotation.RedisLock;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;进口商g.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;/***@ClassName:${}*@Description:TODO*@author:xx@163.com*@日期:*/@RestController@RequestMapping("/business")publicclassBusinessController{@GuaveLock(key="business:arg[0]")@GetMappingpublicStringquery(@RequestParamStringtoken){return"success-"+token;}@RedisLock(prefix="users")@GetMappingpublicStringqueryRedis(@CacheParam(name="token")@RequestParamStringtoken){return"success-"+token;}}Mian函数启动类;将密钥生成策略函数注入@BeanpublicCacheKeyGeneratorcacheKeyGenerator(){returnnewLockKeyGenerator();}