分布式锁的应用场景还是比较多的,然后手头的项目没有有效的封装,然后忙里偷闲,简单封装了一下。
分布式锁的实现方式还是很多,比较多的肯定就是Redis和zk了,现有的项目只有redis,所以以下的分布式锁是基于redis实现的了。
直接上代码吧:
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceRedisLock{//支持SPEL表达式和普通的keyString[]keys();//等待获取锁的时间longtimeout()default3000L;}
/***开启redis分布式锁注解*/@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@ImportAutoConfiguration({LockAop.class})public@interfaceEnableRedisLock{}
@Order(Integer.MIN_VALUE)@AspectpublicclassLockAop{privatestaticLoggerlogger=LoggerFactory.getLogger(LockAop.class);publicLockAop(RedissonClientredissonClient){this.redissonClient=redissonClient;logger.info("LockAop初始化完成!");}@Pointcut("@annotation(com.bosssoft.nontax3.saas.billCollection.collect.api.common.anno.RedisLock)")publicvoidpointCut(){}privateRedissonClientredissonClient;//获取被拦截方法参数名列表(使用Spring支持类库)ASM机制故定义为常量privatestaticfinalLocalVariableTableParameterNameDiscovererU=newLocalVariableTableParameterNameDiscoverer();//使用SPEL进行key的解析线程安全所以可复用privatestaticfinalExpressionParserPARSER=newSpelExpressionParser();@Around("pointCut()")publicObjectaround(ProceedingJoinPointpoint)throwsThrowable{RLocklock=null;booleanflag=false;try{MethodSignaturesignature=(MethodSignature)point.getSignature();Methodmethod=signature.getMethod();RedisLockredisLock=method.getAnnotation(RedisLock.class);finalString[]keys=redisLock.keys();finallongtimeout=redisLock.timeout();if(keys.length>0){finalStringprefix=getPrefix(keys,method,point.getArgs());lock=redissonClient.getLock(prefix);finallongl=System.currentTimeMillis();while(true){//尝试获取锁,获取失败等待一会flag=lock.tryLock();if(flag){break;}//等待一会不要尝试那么快sleep();finallongj=System.currentTimeMillis();if(j-l>timeout){break;}}}if(flag){returnpoint.proceed();}else{thrownewBusinessException(LOCK_ERROR);}}finally{if(flag&&lock!=null){try{if(lock.isLocked()){if(lock.isHeldByCurrentThread()){lock.unlock();}}}catch(Exceptione){logger.error("lock解锁失败",e);}}}}privatevoidsleep(){try{Thread.sleep(100L);}catch(InterruptedExceptione){logger.error("中断异常",e);}}privateStringgetPrefix(String[]keys,Methodmethod,Object[]args){StringBuilderbuilder=newStringBuilder();for(Stringkey:keys){if(key.startsWith("#")){builder.append(parseKey(key,method,args)).append(":");}else{builder.append(key).append(":");}}returnbuilder.toString();}/***获取spel的具体值**@return具体的前缀*/privateStringparseKey(Stringkey,Methodmethod,Object[]args){if(StringUtils.isEmpty(key)){returnnull;}String[]paraNameArr=U.getParameterNames(method);//SPEL上下文StandardEvaluationContextcontext=newStandardEvaluationContext();//把方法参数放入SPEL上下文中for(inti=0;i<Objects.requireNonNull(paraNameArr).length;i++){context.setVariable(paraNameArr[i],args[i]);}returnPARSER.parseExpression(key).getValue(context,String.class);}
只实现了一些基本功能,然后使用了Spring的spel表达式,接来下来看看使用吧
@RedisLock(keys={"#sortTransfer.agencyIdCode","#sortTransfer.directoryCode","save"})publicvoidsaveData(SortTransfersortTransfer){//Dosomething、、、}
spel表达式可以直接解析,这样使用就很便利了。