Files
emo-grab/src/main/java/quant/rich/emoney/component/CallerLockAspect.java

80 lines
2.9 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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();
}
}
/**
* 在方法上添加此注解,可针对调用方加锁,即:<br>
* 调用方法为 A 的,多次从 A 调用则加锁,从 B 调用时不受影响<br>
* 需要开启 AOP在任意配置类上增加注解<i><code>@EnableAspectJAutoProxy</code></i>
* @see CallerLockAspect
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public static @interface LockByCaller {
/**
* 可选参数,用于 SpEL 表达式获取 key
* 例如:<ul>
* <li>@LockByCaller(key = "#userId")</li>
* <li>@LockByCaller(key = "#userId + ':' + #userName")</li>
* </ul>
* 当不指定时,不校验参数,单纯校验 Caller
*/
String key() default "";
}
}