1. 创建自定义注解
首先,我们创建一个自定义注解 @Idempotent,它可以应用于方法上,用来标识该方法需要幂等性保障。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // 该注解应用于方法
@Retention(RetentionPolicy.RUNTIME) // 运行时有效
public @interface Idempotent {
String value() default ""; // 可以设置一个自定义值,作为幂等性判断的条件(如请求ID、操作类型等)
}
2. 创建幂等性处理逻辑
幂等性通常需要结合某些方式来判断,比如请求ID或者事务ID,确保每个操作只执行一次。我们可以利用 AOP(面向切面编程)来在方法执行前后进行拦截,从而判断该方法是否已经执行过。
假设我们使用一个缓存系统(如Redis)来存储已执行的操作ID(比如请求的唯一标识),当请求ID已存在时,就不再执行操作。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
@Component
@Aspect
public class IdempotentAspect {
@Autowired
private RedisService redisService; // 假设我们有一个 Redis 服务用来存储请求ID
@Around("@annotation(idempotent)") // 监听所有标记了 @Idempotent 的方法
public Object handleIdempotency(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {
String requestId = extractRequestId(joinPoint); // 从请求中提取唯一ID
String cacheKey = "idempotent:" + DigestUtils.md5DigestAsHex(requestId.getBytes());
// 判断该请求是否已经执行过
if (redisService.exists(cacheKey)) {
throw new IllegalStateException("Request has already been processed.");
}
// 执行目标方法
Object result = joinPoint.proceed();
// 将请求ID存储到 Redis 中,设置过期时间以便一定时间后可以重新执行
redisService.set(cacheKey, "processed", 3600); // 过期时间为1小时
return result;
}
private String extractRequestId(ProceedingJoinPoint joinPoint) {
// 这里可以根据具体情况提取请求ID,例如从请求头、参数或上下文中
// 假设从方法参数中提取第一个参数作为请求ID
Object[] args = joinPoint.getArgs();
return args.length > 0 ? args[0].toString() : "";
}
}
3. RedisService 示例
为了模拟 Redis 服务,我们可以写一个简单的 RedisService 类,假设它提供了存取和判断某个缓存键是否存在的功能。
import org.springframework.stereotype.Service;
@Service
public class RedisService {
// 假设使用了某个 Redis 客户端(如 Jedis、Lettuce等)
// 判断缓存中是否存在某个键
public boolean exists(String key) {
// 这里假设返回一个布尔值,表示该键是否已存在
return false; // 这里做一个简化,实际应该通过 Redis 判断
}
// 设置缓存
public void set(String key, String value, int expireTimeInSeconds) {
// 将值存储到 Redis 中,设置过期时间
}
}
4. 在方法中使用注解
使用 @Idempotent 注解来标记需要幂等性判断的方法。例如:
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@PostMapping("/createUser")
@Idempotent(value = "createUser")
public String createUser(@RequestBody String requestId) {
// 执行用户创建操作
return "User created successfully";
}
}
5. 总结
- 自定义注解
@Idempotent用于标记需要幂等性判断的方法。 - 使用 AOP 处理逻辑,通过缓存(如 Redis)来判断某个操作是否已执行过。
- 每个请求通过唯一标识(如请求ID)来确保只有第一次请求能执行操作,后续请求被忽略。
- 使用缓存的方式避免对数据库或其他资源进行不必要的多次操作。

684

被折叠的 条评论
为什么被折叠?



