但是每次使用分布式锁都要写类似上面的重复代码,有没有什么方法可以只关注核心业务逻辑代码的编写,即上面的"do some business"。下面介绍如何使用Spring AOP来实现这一目标。
使用Spring AOP简化分布式锁
定义注解@DistributedLock
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
/**
-
锁的名称。
-
如果lockName可以确定,直接设置该属性。
*/
String lockName() default “”;
/**
- lockName后缀
*/
String lockNamePre() default “”;
/**
- lockName后缀
*/
String lockNamePost() default “lock”;
/**
-
获得锁名时拼接前后缀用到的分隔符
-
@return
*/
String separator() default “.”;
/**
-
获取注解的方法参数列表的某个参数对象的某个属性值来作为lockName。因为有时候lockName是不固定的。
-
当param不为空时,可以通过argNum参数来设置具体是参数列表的第几个参数,不设置则默认取第一个。
*/
String param() default “”;
/**
- 将方法第argNum个参数作为锁
*/
int argNum() default 0;
/**
-
是否使用公平锁。
-
公平锁即先来先得。
*/
boolean fairLock() default false;
/**
- 是否使用尝试锁。
*/
boolean tryLock() default false;
/**
-
最长等待时间。
-
该字段只有当tryLock()返回true才有效。
*/
long waitTime() default 30L;
/**
-
锁超时时间。
-
超时时间过后,锁自动释放。
-
建议:
-
尽量缩简需要加锁的逻辑。
*/
long leaseTime() default 5L;
/**
- 时间单位。默认为秒。
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
定义切面代码
@Aspect
@Component
public class DistributedLockAspect {
@Autowired
private DistributedLockTemplate lockTemplate;
@Pointcut(“@annotation(cn.sprinkle.study.distributedlock.common.annotation.DistributedLock)”)
public void DistributedLockAspect() {}
@Around(value = “DistributedLockAspect()”)
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
//切点所在的类
Class targetClass = pjp.getTarget().getClass();
//使用了注解的方法
String methodName = pjp.getSignature().getName();
Class[] parameterTypes = ((MethodSignature)pjp.getSignature()).getMethod().getParameterTypes();
Method method = targetClass.getMethod(methodName, parameterTypes);
Object[] arguments = pjp.getArgs();
final String lockName = getLockName(method, arguments);
return lock(pjp, method, lockName);
}
@AfterThrowing(value = “DistributedLockAspect()”, throwing=“ex”)
public void afterThrowing(Throwable ex) {
throw new RuntimeException(ex);
}
public String getLockName(Method method, Object[] args) {
Objects.requireNonNull(method);
DistributedLock annotation = method.getAnnotation(DistributedLock.class);
String lockName = annotation.lockName(),
param = annotation.param();
if (isEmpty(lockName)) {
if (args.length > 0) {
if (isNotEmpty(param)) {
Object arg;
if (annotation.argNum() > 0) {
arg = args[annotation.argNum() - 1];
} else {
arg = args[0];
}
lockName = String.valueOf(getParam(arg, param));
} else if (annotation.argNum() > 0) {
lockName = args[annotation.argNum() - 1].toString();
}
}
}
if (isNotEmpty(lockName)) {
String preLockName = annotation.lockNamePre(),
postLockName = annotation.lockNamePost(),
separator = annotation.separator();
StringBuilder lName = new StringBuilder();
if (isNotEmpty(preLockName)) {
lName.append(preLockName).append(separator);
}
lName.append(lockName);
if (isNotEmpty(postLockName)) {
lName.append(separator).append(postLockName);
}
lockName = lName.toString();