基于之前用redis的lua脚本来实现安全的分布式锁,发现代码是加锁虽然灵活,但是非常的不便捷。每次需要加锁的时候,都要写出非常多的重复性代码。遂…………
基于之前用redis的lua脚本来实现安全的分布式锁,发现代码是加锁虽然灵活,但是非常的不便捷。每次需要加锁的时候,都要写出非常多的重复性代码。遂考虑利用AOP的方式,完成这一重复性的工作。
在没利用注解之前加锁方式如下,基本每次都要这样写
1 2 3 4 5 6 7 8 9 10 11 12 13
| String uuid = UUID.uuid(); try { boolean getLock = RedisLockUtil.tryGetDistributedLock(key, uuid, 5000); if (getLock) {
} } finally { RedisLockUtil.releaseDistributedLock(key, uuid); }
|
基于注解的使用放入如下,比较便捷
1 2 3 4 5
| @Locker(key = RedisKeyEnum.POOL_ORDER_LOCK, paramExp = "0", noGetMsg = "老铁来晚了!") public GrabAndAnswerVo grabOrderAnswer(String orderId, RedisKeyEnum poolType, User currentUser) { Long workId = orderExist(poolType, orderId); return doctorGrabOrderAnswer(poolType, orderId, currentUser); }
|
- 首先AOP的使用方式我定义为利用注解来判断是否需要加锁,类似事务的方式,我们定义一个Locker注解,这个注解的功能可以看代码;
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
|
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Locker {
RedisKeyEnum key();
String paramExp();
long expireTime() default 10000;
long limitTime() default 0;
boolean continueGet() default false;
int maxGetNum() default 0;
String noGetMsg() default "未获取锁"; }
|
- 实现AOP的拦截规则
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 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
|
@Aspect @Component public class LockerAspect {
private static Logger LOGGER = LoggerFactory.getLogger(LockerAspect.class);
@Pointcut("@annotation(com.ym.common.utils.annotation.Locker)") public void pointcut() { }
@Around("pointcut()") public Object around(ProceedingJoinPoint joinPoint) { Object proceed = null; long startTime = System.currentTimeMillis(); Locker locker = getAnnotation(joinPoint, Locker.class); Object[] args = joinPoint.getArgs(); int maxGetNum = locker.maxGetNum(); String uuid = UUID.uuid(); String lockFiled = getLockFiled(args, locker.paramExp()); String lockKey = RedisKeyUtil.keyBuilder(locker.key(), lockFiled); long expireTime = locker.expireTime(); boolean lock = RedisLockUtil.tryGetDistributedLock(lockKey, uuid, expireTime); int getNum = 0; while (!lock && locker.continueGet() && (maxGetNum == 0 || getNum < maxGetNum)) { Threads.sleep(100); lock = RedisLockUtil.tryGetDistributedLock(lockKey, uuid, expireTime); } if (!lock) { throw new BusinessException(locker.noGetMsg()); } try { proceed = joinPoint.proceed(); long sleepTime = locker.limitTime() - (System.currentTimeMillis() - startTime); if (sleepTime > 0) { Threads.sleep(sleepTime); } } catch (Throwable throwable) { throwable.printStackTrace(); } finally { RedisLockUtil.releaseDistributedLock(lockKey, uuid); } return proceed; }
private String getLockFiled(Object[] args, String expression) { if (args == null || args.length == 0 || StringUtils.isBlank(expression)) { throw new UnsupportedOperationException("Locker所在方法参数为空! 请使用代码锁"); } String[] extraParams = expression.split("\\?"); String extraKey = null; if (extraParams.length > 1) { extraKey = extraParams[1]; expression = extraParams[0]; } String[] commboExpression = expression.split("\\+"); StringBuilder field = new StringBuilder(); for (String commbo : commboExpression) { String[] split = commbo.split("#"); int argsNum = 0; try { if (split.length == 1) { argsNum = Integer.parseInt(split[0]); field.append(String.valueOf(args[argsNum])); } else { argsNum = Integer.parseInt(split[0]); Object fieldValue = ReflectUtils.getFieldValue(args[argsNum], split[1]); field.append(String.valueOf(fieldValue)); } } catch (Exception e) { throw new UnsupportedOperationException("Locker表达式paramExp不正确!"); } } if (extraKey != null) { field.append(extraKey); } return field.toString(); }
private <T> T getAnnotation(JoinPoint joinPoint, Class<? extends Annotation> t) { Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); if (method != null) { return (T) method.getAnnotation(t); } return null; } }
|