java 自定义注解实现幂等性判断

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)来确保只有第一次请求能执行操作,后续请求被忽略。
  • 使用缓存的方式避免对数据库或其他资源进行不必要的多次操作。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值