package quant.rich.emoney.component; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.expression.*; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import quant.rich.emoney.util.CallerLockUtil; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; import java.util.concurrent.locks.ReentrantLock; @Aspect @Component public class CallerLockAspect { private final SpelExpressionParser parser = new SpelExpressionParser(); @Around("@annotation(quant.rich.emoney.component.CallerLockAspect.LockByCaller)") public Object around(ProceedingJoinPoint pjp) throws Throwable { MethodSignature signature = (MethodSignature) pjp.getSignature(); Method method = signature.getMethod(); LockByCaller annotation = method.getAnnotation(LockByCaller.class); // 获取 SpEL key(如果有) Object[] args = pjp.getArgs(); String[] paramNames = signature.getParameterNames(); Object[] extraKey = new Object[0]; if (!annotation.key().isEmpty()) { EvaluationContext context = new StandardEvaluationContext(); for (int i = 0; i < paramNames.length; i++) { context.setVariable(paramNames[i], args[i]); } Expression expression = parser.parseExpression(annotation.key()); Object value = expression.getValue(context); extraKey = (value == null) ? new Object[0] : new Object[]{value}; } // 复用工具类的锁逻辑 ReentrantLock lock = CallerLockUtil.acquireLock(extraKey); lock.lock(); try { return pjp.proceed(); } finally { lock.unlock(); } } /** * 在方法上添加此注解,可针对调用方加锁,即:
* 调用方法为 A 的,多次从 A 调用则加锁,从 B 调用时不受影响
* 需要开启 AOP:在任意配置类上增加注解:@EnableAspectJAutoProxy * @see CallerLockAspect */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public static @interface LockByCaller { /** * 可选参数,用于 SpEL 表达式获取 key * 例如: * 当不指定时,不校验参数,单纯校验 Caller */ String key() default ""; } }