RepeatSubmitAspect.java 5.29 KB
package com.infoloop.tianting.aspect;

import com.infoloop.tianting.annotation.RepeatSubmit;
import com.infoloop.tianting.annotation.RequestKeyParam;
import com.infoloop.tianting.constant.CommonConstants;
import com.infoloop.tianting.context.LoginContextHolder;
import com.infoloop.tianting.exception.ClientEndExceptions;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

@Aspect
@Configuration
@Order(1024)
@Slf4j
public class RepeatSubmitAspect {

    private final RedissonClient redissonClient;

    @Autowired
    public RepeatSubmitAspect(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Around("execution(public * * (..)) && @annotation(com.infoloop.tianting.annotation.RepeatSubmit)")
    public Object interceptor(ProceedingJoinPoint joinPoint) throws Throwable {
        final var methodSignature = (MethodSignature) joinPoint.getSignature();
        final var method = methodSignature.getMethod();
        final var requestLock = method.getAnnotation(RepeatSubmit.class);
        final var lockKey = getLockKey(joinPoint);
        final var lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(requestLock.expire(), requestLock.timeUnit())) {
                return joinPoint.proceed();
            } else {
                log.error("Failed to acquire lock for method: {}", method.getName());
                throw ClientEndExceptions.ResourceLocked.build(requestLock.message());
            }
        } catch (InterruptedException e) {
            log.error("Interrupted while trying to acquire lock for method: {}", method.getName(), e);
        } finally {
            if (lock.isHeldByCurrentThread() && requestLock.methodAutoUnlock()) {
                lock.unlock();
            }
        }
        return null;
    }

    /**
     * 获取LockKey
     *
     * @param joinPoint 切入点
     * @return
     */
    public static String getLockKey(ProceedingJoinPoint joinPoint) {
        //获取连接点的方法签名对象
        final var methodSignature = (MethodSignature) joinPoint.getSignature();
        //Method对象
        final var method = methodSignature.getMethod();
        //获取Method对象上的注解对象
        final var requestLock = method.getAnnotation(RepeatSubmit.class);
        //获取方法参数
        final var args = joinPoint.getArgs();
        //获取Method对象上所有的注解
        final var parameters = method.getParameters();
        final var sb = new StringBuilder();
        for (var i = 0; i < parameters.length; i++) {
            final var keyParam = parameters[i].getAnnotation(RequestKeyParam.class);
            //如果属性不是RequestKeyParam注解,则不处理
            if (keyParam == null) {
                continue;
            }
            //如果属性是RequestKeyParam注解,则拼接 连接符 "& + RequestKeyParam"
            if (sb.length() > 0) {
                sb.append(requestLock.delimiter());
            }
            sb.append(args[i]);
        }
        //如果方法上没有加RequestKeyParam注解
        if (StringUtils.isEmpty(sb.toString())) {
            //获取方法上的多个注解(为什么是两层数组:因为第二层数组是只有一个元素的数组)
            final var parameterAnnotations = method.getParameterAnnotations();
            //循环注解
            for (var i = 0; i < parameterAnnotations.length; i++) {
                final var object = args[i];
                //获取注解类中所有的属性字段
                final var fields = object.getClass().getDeclaredFields();
                for (var field : fields) {
                    //判断字段上是否有RequestKeyParam注解
                    final var annotation = field.getAnnotation(RequestKeyParam.class);
                    //如果没有,跳过
                    if (annotation == null) {
                        continue;
                    }
                    //如果有,设置Accessible为true(为true时可以使用反射访问私有变量,否则不能访问私有变量)
                    field.setAccessible(true);
                    //如果属性是RequestKeyParam注解,则拼接 连接符" & + RequestKeyParam"
                    if (sb.length() > 0) {
                        sb.append(requestLock.delimiter());
                    }
                    sb.append(ReflectionUtils.getField(field, object));
                }
            }
        }
        if (requestLock.isLockByLoginUser()) {
            final var operatorId = LoginContextHolder.hasLogin() ? LoginContextHolder.getId() : null;
            return requestLock.prefix() + (operatorId != null ? CommonConstants.COLON + operatorId : "") + (sb.length() > 0 ? CommonConstants.UNDERLINE + sb : "");
        } else {
            return requestLock.prefix() + (sb.length() > 0 ? CommonConstants.UNDERLINE + sb : "");
        }
    }
}