项目-网盘-AOPredis分布式锁

项目AOP 幂等性

注意:是通过自定义注释实现的,在需要实现幂等性的方法上添加注释

一、准备

  1. 自定义注解 @Idempotent,用于标识需要幂等性控制的方法。

image-20240519161422839

  1. 定义AOP切面类
  • 使用 @Aspect 定义切面,拦截使用 @Idempotent 注解的方法,确保幂等性。

image-20240519161544023

二、调用

1.在需要幂等性的接口方法上使用这个注释

image-20240519162009066

2.在应用启动的时候,spring会自动的扫描并注册aop切面类’**IdempotentAopHandler**

  • 当调用标记了 @Idempotent 注解的方法时,AOP 切面会拦截调用,检查 Redis 中是否存在锁,如果不存在则设置锁并执行方法,执行完毕后释放锁。

3.获取唯一的标识

  • @Idempotent 注解中获取 uniqueIdentification,结合当前用户的 Token 生成幂等性锁标识。

4.尝试获得锁

    • 调用 redisUtil.setIfAbsentEx 方法尝试在 Redis 中设置一个带有过期时间的键,如果设置成功,则表示获取到锁。

5.执行方法

  • 获取锁成功后,执行目标方法。
  • 如果方法执行过程中抛出异常,记录日志并抛出自定义异常。

6.释放锁

  • 方法执行完毕后,无论是否成功,都会在 finally 块中删除 Redis 锁,确保锁被释放。

7.异常处理

  • 如果未能获取到锁,抛出自定义异常,表示该方法在指定时间内不能重复执行。

通过这种方式,可以确保方法在指定的时间窗口内只会执行一次,防止重复提交或重复调用,保证接口的幂等性。

三、代码

在代码里是在AOP切面编程中,使用了redis的分布式锁来实现最终一个结果,就是redis的操作的幂等性,这个redis只是我的猜测,保证在并发情况下只执行一次

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
package top.quhailong.pan.framework.redis.core.idempotent.aop;
@Order(1)// 设置切面的执行顺序,值越小优先级越高
@Aspect//表明这个类是一个切面类
@Slf4j//引入日志记录功能
class IdempotentAopHandler {
@Autowired
private RedisUtil redisUtil;
/**
* 设置操作日志切入点 记录操作日志 在注解的位置切入代码
@annotation(top.quhailong.pan.framework.redis.core.idempotent.annotation.Idempotent) 标识将匹配,即,将会在所有标注了这个注释的方法拦截,并调用aop
*/
@Pointcut("@annotation(top.quhailong.pan.framework.redis.core.idempotent.annotation.Idempotent)")
public void IdempotentPointCut() {
}
// 通过定义切入点,环绕通知(@Around 注解标注的方法)可以使用该切入点来指定在哪些方法执行前后进行额外的处理。
/**
* 环绕通知,接口幂等
* @author: quhailong
* @date: 2020/7/26
* round定义了一个环绕通知,围绕切点定义的方法的前后
*/









// 这里需要处理幂等性控制逻辑
// 通过这个注释,知道要在哪里拦截,那么就通过反射得到被拦截的方法和它的注释信息,
//MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();

//Method method = signature.getMethod();
// 通过这个方式获得方法(被拦截的方法)
@Around("IdempotentPointCut()")
public Object addIdempotent(ProceedingJoinPoint proceedingJoinPoint) {
Object result = null;


//获取方法签名。
// MethodSignature 和 Method:通过反射获取被拦截的方法和其注解信息。
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
// 获取切入点所在的方法,获得方法对象,签名里包含了
Method method = signature.getMethod();


// 获取操作 dempotent 注解:获取注解实例,并读取 uniqueIdentification 属性,用于生成唯一的幂等性锁标识。
// 注解实例是 Java 中表示注解的对象。通过反射机制,可以在运行时获取这些注解实例,并读取注解中的属性值。以下是如何定义和使用自定义注解的示例:
Idempotent idempotent = method.getAnnotation(Idempotent.class);
String uniqueIdentification = idempotent.uniqueIdentification();
//uniqueIdentification 是 @Idempotent 注解中的一个属性,通过注解实例可以访问该属性的值。



// idempotentLock:构建 Redis 锁的键,使用唯一标识符和当前用户的 Token。
String idempotentLock = String.format(RedisConstants.IDEMPOTENT_LOCK, uniqueIdentification, TokenUtil.getToken());

// redisUtil.setIfAbsentEx( 尝试在redis中设置一个带有过期时间的键,如果设置成功,表示获得了锁
if (redisUtil.setIfAbsentEx(idempotentLock, UUID.randomUUID().toString(), 120, TimeUnit.SECONDS)) {
try {
result = proceedingJoinPoint.proceed();
// proceedingJoinPoint.proceed():执行目标方法。
// 这里就是分布式的关键,需要得到锁,不然就不给执行
} catch (Throwable e) {
log.error("业务处理出现异常", e);
throw new CustomException(ResultCodeEnum.SERVICE_EXCEPTION.getCode(), ResultCodeEnum.SERVICE_EXCEPTION.getDesc());
} finally {
redisUtil.delete(idempotentLock);
// 删除redis的锁
}
} else {
throw new CustomException(ResultCodeEnum.IDEMPOTENT_LOCK_ERROR.getCode(), ResultCodeEnum.IDEMPOTENT_LOCK_ERROR.getMessage());
}
return result;
}
}

四、一些细节

方法签名

方法签名是编程中描述一个方法的特征的方式,它标识方法的名称以及方法的参数的类型顺序,在java中,方法签名不包括方法的返回类型和访问修饰符,

image-20240519164704389

image-20240519164717586

作用

AOP框架中,就是可以通过方法签名来识别和拦截方法的调用