80 lines
2.9 KiB
Java
80 lines
2.9 KiB
Java
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 "";
|
||
}
|
||
} |