From fc835290dce3a77732d215075b1b367e08dc2cee Mon Sep 17 00:00:00 2001 From: pbvfus8to <480171784@qq.com> Date: Wed, 18 Dec 2024 11:07:54 +0800 Subject: [PATCH] Update RedisLockAspect.java --- .../shop/common/aspect/RedisLockAspect.java | 98 +++++++++++++------ 1 file changed, 66 insertions(+), 32 deletions(-) diff --git a/yami-shop-common/src/main/java/com/yami/shop/common/aspect/RedisLockAspect.java b/yami-shop-common/src/main/java/com/yami/shop/common/aspect/RedisLockAspect.java index e47cee7..b7f74ac 100644 --- a/yami-shop-common/src/main/java/com/yami/shop/common/aspect/RedisLockAspect.java +++ b/yami-shop-common/src/main/java/com/yami/shop/common/aspect/RedisLockAspect.java @@ -27,48 +27,82 @@ import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** + * Redis锁切面类 + * 该类利用Spring AOP(面向切面编程)机制,通过定义切面和切点,实现了基于Redis锁的并发控制功能。 + * 主要作用是在被标注了`@RedisLock`注解的方法执行前后进行加锁和解锁操作,以保证在同一时刻只有一个线程能够执行被标注的方法,避免并发冲突。 + * * @author lgh */ @Aspect +// 使用 @Aspect 注解声明该类是一个切面类,用于定义切点和增强逻辑(如前置通知、后置通知、环绕通知等) @Component +// 使用 @Component 注解将该类注册为Spring容器中的一个组件,方便进行依赖注入等操作 public class RedisLockAspect { - @Autowired - private RedissonClient redissonClient; + @Autowired + // 通过依赖注入获取RedissonClient对象,用于与Redis进行交互,操作Redis锁相关的功能 + private RedissonClient redissonClient; + + // 定义Redis锁的键名前缀,用于在Redis中区分不同的锁,方便管理和识别 + private static final String REDISSON_LOCK_PREFIX = "redisson_lock:"; - private static final String REDISSON_LOCK_PREFIX = "redisson_lock:"; + /** + * 环绕通知方法,用于在被标注`@RedisLock`注解的方法执行前后添加加锁和解锁逻辑 + * 该方法在目标方法执行前获取Redis锁,在目标方法执行完毕后释放锁,确保在同一时刻只有一个线程能够执行被标注的方法,实现并发控制。 + * + * @param joinPoint 连接点对象,代表了目标方法的执行点,通过它可以获取目标方法的相关信息,如方法签名、参数等。 + * @param redisLock 从目标方法上获取的`@RedisLock`注解对象,通过它可以获取注解中定义的锁相关的配置信息,如锁的键表达式、锁名称、过期时间等。 + * @return 返回目标方法执行的结果,即被标注`@RedisLock`注解的方法正常执行后的返回值。 + * @throws Throwable 如果在目标方法执行过程中或者获取、释放锁的过程中出现异常,会将异常向上抛出。 + */ + @Around("@annotation(redisLock)") + // 使用 @Around 注解定义环绕通知,该通知会在目标方法执行前后进行额外的逻辑处理,这里就是围绕着被标注`@RedisLock`注解的方法进行加锁和解锁操作 + public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable { + // 获取`@RedisLock`注解中定义的用于生成锁键的SPEL表达式,SPEL表达式可以根据方法参数等动态生成锁键 + String spel = redisLock.key(); + // 获取`@RedisLock`注解中定义的锁名称,用于更直观地标识锁的用途等信息 + String lockName = redisLock.lockName(); - @Around("@annotation(redisLock)") - public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable { - String spel = redisLock.key(); - String lockName = redisLock.lockName(); + // 根据连接点、锁名称以及SPEL表达式生成最终的Redis锁的键名,并通过RedissonClient获取对应的Redis锁对象 + RLock rLock = redissonClient.getLock(getRedisKey(joinPoint, lockName, spel)); - RLock rLock = redissonClient.getLock(getRedisKey(joinPoint,lockName,spel)); + // 使用获取到的Redis锁对象进行加锁操作,设置锁的过期时间和时间单位,按照`@RedisLock`注解中定义的配置来进行 + rLock.lock(redisLock.expire(), redisLock.timeUnit()); - rLock.lock(redisLock.expire(),redisLock.timeUnit()); + Object result = null; + try { + // 执行被标注`@RedisLock`注解的目标方法,即让原本的业务逻辑正常执行 + result = joinPoint.proceed(); - Object result = null; - try { - //执行方法 - result = joinPoint.proceed(); + } finally { + // 无论目标方法执行是否成功,最终都要释放锁,确保锁资源能够被正确释放,避免死锁等问题 + rLock.unlock(); + } + return result; + } - } finally { - rLock.unlock(); - } - return result; - } + /** + * 将SPEL表达式转换为具体的Redis锁键字符串的方法 + * 该方法根据连接点(包含目标方法、目标对象以及方法参数等信息)、锁名称以及SPEL表达式,通过解析SPEL表达式,结合相关信息生成最终用于在Redis中标识锁的键字符串。 + * + * @param joinPoint 切点对象,代表了目标方法的执行点,从中可以获取目标方法、目标对象以及方法参数等信息,用于解析SPEL表达式。 + * @param lockName 锁的名称,作为生成的Redis锁键的一部分,用于更直观地标识锁的用途等信息。 + * @param spel 用于生成锁键的SPEL表达式,通过解析该表达式,结合目标方法、目标对象以及方法参数等信息来生成最终的锁键字符串。 + * @return 返回生成的Redis锁键字符串,格式为"redisson_lock:锁名称:具体解析后的表达式内容",用于在Redis中唯一标识一个锁。 + */ + private String getRedisKey(ProceedingJoinPoint joinPoint, String lockName, String spel) { + // 获取连接点对应的方法签名信息,用于后续获取目标方法对象 + Signature signature = joinPoint.getSignature(); + // 将方法签名信息转换为方法签名的具体实现类对象,方便获取目标方法的详细信息 + MethodSignature methodSignature = (MethodSignature) signature; + // 获取目标方法对象,后续可用于解析SPEL表达式等操作 + Method targetMethod = methodSignature.getMethod(); + // 获取目标对象,即被代理的实际业务对象,同样可用于解析SPEL表达式等操作 + Object target = joinPoint.getTarget(); + // 获取目标方法的参数数组,这些参数在解析SPEL表达式时可能会作为变量参与计算 + Object[] arguments = joinPoint.getArgs(); - /** - * 将spel表达式转换为字符串 - * @param joinPoint 切点 - * @return redisKey - */ - private String getRedisKey(ProceedingJoinPoint joinPoint,String lockName,String spel) { - Signature signature = joinPoint.getSignature(); - MethodSignature methodSignature = (MethodSignature) signature; - Method targetMethod = methodSignature.getMethod(); - Object target = joinPoint.getTarget(); - Object[] arguments = joinPoint.getArgs(); - return REDISSON_LOCK_PREFIX + lockName + StrUtil.COLON + SpelUtil.parse(target,spel, targetMethod, arguments); - } -} + // 通过工具类方法解析SPEL表达式,结合锁名称等信息生成最终的Redis锁键字符串,并添加前缀,形成完整的用于在Redis中标识锁的键名 + return REDISSON_LOCK_PREFIX + lockName + StrUtil.COLON + SpelUtil.parse(target, spel, targetMethod, arguments); + } +} \ No newline at end of file