分布式锁是实际应用中最常见的场景之一,Redisson虽然支持了分布式锁实现,但是使用上仍然不够方便。本文介绍了一种优雅的分布式锁的静态方法封装和注解式封装实现。
静态方法封装
为了方便在业务代码中使用分布式锁,可以通过静态方法直接调用来实现。结合函数式编程,完整接口定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
public static Object lockExecute(String key, Long leaseTimeMs, Executable executable);
public static void lockRun(String key, Long leaseTimeMs, Runnable runnable);
|
lockExecute()
支持返回分布式锁加锁执行结果,lockRun()
则无返回结果。
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
|
@UtilityClass @Slf4j public class RLockUtils {
@SneakyThrows public static Object lockExecute(String key, Long leaseTimeMs, Executable executable) { RLock lock = doLock(key, leaseTimeMs); try { return executable.execute(); } finally { doUnlock(key, leaseTimeMs, lock); } }
public static void lockRun(String key, Long leaseTimeMs, Runnable runnable) { RLock lock = doLock(key, leaseTimeMs); try { runnable.run(); } finally { doUnlock(key, leaseTimeMs, lock); } }
private static void doUnlock(String key, Long leaseTimeMs, RLock lock) { if (lock != null) { try { lock.unlock(); } catch (Exception e) { log.error("分布式锁释放失败!不影响业务执行!key={}, leaseTimeMs={}", key, leaseTimeMs, e); } } }
private static RLock doLock(String key, Long leaseTimeMs) { RLock lock = null; try { RedissonClient redissonClient = SpringUtil.getBean(RedissonClient.class); lock = redissonClient.getLock(key); lock.lock(leaseTimeMs, TimeUnit.MILLISECONDS); } catch (Exception e) { log.error("分布式锁加锁失败!不影响业务正常执行!key={}, leaseTimeMs={}", key, leaseTimeMs, e); } return lock; }
}
|
注解式封装
注解式编程是日常工作中更常用的一种方式,下面重点介绍注解式的实现方案。
定义注解@RLock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RLock {
String key();
long leaseTimeMs() default 5_000; }
|
定义切面实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| @Aspect @Component @Slf4j @Order(0) public class RLockAspect {
ExpressionParser parser = new SpelExpressionParser();
private final PropertyPlaceholderHelper defaultHelper = new PropertyPlaceholderHelper("#{", "}");
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
@Around("@annotation(rLock)") public Object lock(ProceedingJoinPoint joinPoint, RLock rLock) throws Throwable { String key; try { key = generateKey(joinPoint, rLock); } catch (Exception e) { log.error("生成分布式锁标识失败,执行单元将会直接执行!"); return joinPoint.proceed(); } return RLockUtils.lockExecute(key, rLock.leaseTimeMs(), joinPoint::proceed); }
private String generateKey(ProceedingJoinPoint joinPoint, RLock rLock) { String key = rLock.key(); Assert.hasText(key, "key不能配置空白字符!"); EvaluationContext context = new StandardEvaluationContext(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Object[] args = joinPoint.getArgs(); String[] parameterNames = parameterNameDiscoverer.getParameterNames(method); if (parameterNames != null && parameterNames.length > 0) { for (int i = 0; i < parameterNames.length; i++) { context.setVariable(parameterNames[i], args[i]); } } return defaultHelper.replacePlaceholders(key, placeholderName -> { Expression expression = parser.parseExpression("#" + placeholderName); return String.valueOf(expression.getValue(context)); }); }
}
|
切面将会自动拦截所有标注了@RLock
的方法,并织入分布式锁的代码。重点有3个Spring
相关类需要关注一下:
使用
在任意方法上,标注@RLock
即可自动织入分布式锁:
1 2 3 4 5 6
| @RLock(key = "abc_#{id}_#{person.name}_#{person.age}") @SneakyThrows public void testRLock(Long id, Person person) { Thread.sleep(5000L); }
|
原创不易,觉得文章写得不错的小伙伴,点个赞👍 鼓励一下吧~
欢迎关注我的开源项目:一款适用于SpringBoot的轻量级HTTP调用框架