网站排名优化公司哪家好,河北城乡建设学校官方网站,富阳网站seo价格,wordpress主题知更鸟美化前言
在项目中#xff0c;经常需要使用Redisson分布式锁来保证并发操作的安全性。在未引入基于注解的分布式锁之前#xff0c;我们需要手动编写获取锁、判断锁、释放锁的逻辑#xff0c;导致代码重复且冗长。为了简化这一过程#xff0c;我们引入了基于注解的分布式锁经常需要使用Redisson分布式锁来保证并发操作的安全性。在未引入基于注解的分布式锁之前我们需要手动编写获取锁、判断锁、释放锁的逻辑导致代码重复且冗长。为了简化这一过程我们引入了基于注解的分布式锁通过一个注解就可以实现获取锁、判断锁、处理完成后释放锁的逻辑。这样可以大大简化代码提高开发效率。
目标
使用DistributedLock即可实现获取锁判断锁处理完成后释放锁的逻辑。
RestController
public class HelloController {DistributedLockGetMapping(/helloWorld)public void helloWorld() throws InterruptedException {System.out.println(helloWorld);Thread.sleep(100000);}
}涉及知识
SpringBootSpring AOPRedisson自定义注解统一异常处理SpEL表达式
代码实现
引入依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId
/dependency
dependencygroupIdorg.redisson/groupIdartifactIdredisson/artifactIdversion3.21.3/version
/dependency
dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId
/dependency注解类
/*** 分布式锁注解* author 只有影子*/
Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
public interface DistributedLock {/*** 获取锁失败时默认的错误描述*/String errorDesc() default 任务正在处理中请耐心等待;/*** SpEL表达式用于获取锁的key* 示例* #name则从方法参数中获取name的值作为key* #user.id则从方法参数中获取user对象中的id作为key*/String[] keys() default {};/*** key的前缀为空时取类名方法名*/String prefix() default ;
}切面类
/*** 分布式锁切面类* author 只有影子*/
Slf4j
Aspect
Component
public class DistributedLockAspect {Resourceprivate RedissonClient redissonClient;private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER new DefaultParameterNameDiscoverer();Around(annotation(distributedLock))public Object around(ProceedingJoinPoint joinPoint,DistributedLock distributedLock) throws Throwable {String redisKey getRedisKey(joinPoint, distributedLock);log.info(拼接后的redisKey为 redisKey);RLock lock redissonClient.getLock(redisKey);if (!lock.tryLock()) {// 可以使用自己的异常类演示用RuntimeExceptionthrow new RuntimeException(distributedLock.errorDesc());}// 执行被切面的方法try {return joinPoint.proceed();} finally {lock.unlock();}}/*** 动态解密参数拼接redisKey* param joinPoint* param distributedLock 注解* return*/private String getRedisKey(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {MethodSignature signature (MethodSignature) joinPoint.getSignature();Method method signature.getMethod();EvaluationContext context new MethodBasedEvaluationContext(TypedValue.NULL, method, joinPoint.getArgs(), PARAMETER_NAME_DISCOVERER);StringBuilder redisKey new StringBuilder();// 拼接redis前缀if (StringUtil.isNotBlank(distributedLock.prefix())) {redisKey.append(distributedLock.prefix()).append(:);} else {// 获取类名String className joinPoint.getTarget().getClass().getSimpleName();// 获取方法名String methodName joinPoint.getSignature().getName();redisKey.append(className).append(:).append(methodName).append(:);}ExpressionParser parser new SpelExpressionParser();for (String key : distributedLock.keys()) {// keys是个SpEL表达式Expression expression parser.parseExpression(key);Object value expression.getValue(context);redisKey.append(ObjectUtils.nullSafeToString(value));}return redisKey.toString();}
}统一异常处理类
/*** 全局异常处理类* author 只有影子*/
RestControllerAdvice
public class ExceptionHandle {ExceptionHandler(Exception.class)ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)public String sendErrorResponseSystem(Exception e) {// 这里只是模拟返回值实际项目中一般都是返回封装好的统一返回类return e.getMessage();}
}还需要将redis配置读入这里就不体现 使用示例
1. 无参方法或者需要加方法级的锁
DistributedLock
GetMapping(/helloWorld)
public void helloWorld() throws InterruptedException {System.out.println(helloWorld);Thread.sleep(100000);
}调用接口http://localhost:8080/helloWorld
拼接后的redisKey为HelloController:helloWorld:可以看到无参方法的key为HelloController:helloWorld:其中HelloController为类名helloWorld为方法名因为是无参方法所以没有接下来的参数。
这时候再次调用改接口则不会再进去接口会被切面类直接拦截返回如下结果 在实际生产使用中这种情况一般被用来在自动任务上标注因为在集群环境中自动任务同一时间一般只需要启动一个。 2. 有参数方法其中key从name中取值
DistributedLock(keys #name)
GetMapping(/hello1)
public String hello1(String name) throws InterruptedException {String s hello name;System.out.println(s);Thread.sleep(100000);return s;
}调用接口为http://localhost:8080/hello1?namehurry
拼接后的redisKey为HelloController:hello1:hurry这时候再通过hurry这个名称调用时就不会再处理而name换为zhangsan时则就能正常进入接口。
这时候redis中的key为 127.0.0.16379 connected!keys *
HelloController:hello2:zhangsan
HelloController:hello2:hurry实际业务中需要根据不同的参数值进行加锁的场景。 3. 有参数方法其中key需要从user对象中获取name
DistributedLock(keys #user.name)
GetMapping(/hello2)
public String hello2(User user) throws InterruptedException {String s hello user.getName();System.out.println(s);Thread.sleep(100000);return s;
}需要从某个对象中获取指定属性作为key的场景 4.有参数方法其中key从name上取值并指定前缀
DistributedLock(keys #name,prefix testPrefix)
GetMapping(/hello3)
public String hello3(String name) throws InterruptedException {String s hello name;System.out.println(s);Thread.sleep(100000);return s;
}需要指定key前缀的场景 最后
由于文章篇幅原因很多东西没有深入的讲解但是基于以上代码基本实现了基于注解的分布式锁可以大大提到开发效率。如果还有其他需要拓展的功能可以通过在注解类增加属性及在切面类中通过不同的属性进行不同的处理来实现。