基于自定义注解和SpringEL表达式的分布式锁实现

编程

需求

1、项目中不可避免的需要使用分布式保证幂等。所以一个简单可靠,易用的工具提上日程。

2、演进过程 

  • 最开始使用try finally 块实现。代码臃肿。还要时刻记得释放。
  • 改用回调方式封装锁的获取和释放,但是依然臃肿,需要实现成功和获取锁失败的回调方法。然而获取锁失败几乎都做的一样的事。
  • 使用注解,代价就是使用范围是整个方法。需要自己确认好了使用范围。另外第一版不支持Spring EL。想使用参数值做锁实在太麻烦。
  • 改进注解,使用spring EL引擎。提供强大的数据获取功能。并且对返回值使用调用静态方法和创建新对象十分友好。
  • 我们并没有直接使用spirng EL的所有语法。而是选择包装了一下,因为大家对Spring EL认识参差不齐。

 

demo:

@LockMethod(

value = {

@ExtractParam(paramName = "accountInfo", fieldName = "accountId"),

@ExtractParam(paramName = "order", fieldName = "id"),

@ExtractParam(paramName = "uid")

}

, formatter = "lockTest:%s:%s:%s"

, failureExpression = "new java.util.ArrayList()")

public Object lockTest(TAccountInfo accountInfo, TPayOrder order, Long uid) {

return CommonResultEntity.success();

}

代码:

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Repeatable(value = LockMethod.class)

public @interface ExtractParam {

/**

* 作为锁的参数名称

*/

String paramName();

/**

* 参数的 属性值。可以为空

*/

String fieldName() default "";

}

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface LockMethod {

/**

* 提取的参数

*/

ExtractParam[] value();

/**

* 自定义

*/

String formatter() default "";

/**

* 失败后返回类型

*/

Class<?> failureType() default void.class;

/**

* 失败返回 表达式

*/

String failureExpression() default "";

}

@Aspect

@Component

@Slf4j

public class LockInterceptor {

/**

* spring 参数名称解析器

*/

private static final ParameterNameDiscoverer LOCAL_VARIABLE_TABLE_PARAMETER_NAME_DISCOVERER

= new LocalVariableTableParameterNameDiscoverer();

/**

* spring el 表达式解析解

*/

private static final ExpressionParser SPEL_EXPRESSION_PARSER = new SpelExpressionParser();

/**

* Elvis运算符 在一些编程语言中(比如C#、Kotlin等)提供该功能,语法是?:。意义是当某变量不为空的时候使用该变量,当该变量为空的时候使用指定的默认值。

*/

@Around("@annotation(com.xxx.xxx.support.LockMethod)")

public Object lock(ProceedingJoinPoint pjp) throws Throwable {

String methodName = pjp.getSignature().getName();

Object[] args = pjp.getArgs();

Class<?> classTarget = pjp.getTarget().getClass();

Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();

Method targetMethod = classTarget.getMethod(methodName, par);

String[] parameterNames = LOCAL_VARIABLE_TABLE_PARAMETER_NAME_DISCOVERER.getParameterNames(targetMethod);

LockMethod lockMethod = targetMethod.getAnnotation(LockMethod.class);

final String lockName = parseLockName(args, lockMethod, parameterNames);

log.info("lockName={} act=LockInterceptor", lockName);

return doLock(pjp, lockMethod, lockName);

}

private Object doLock(ProceedingJoinPoint pjp, LockMethod lockMethod, String lockName) {

return DistributedLock.acquireLock(MDCUtils.getLogStr(), lockName, new LockCallback<Object>() {

@Override

public Object onSuccess(String logStr) {

try {

return pjp.proceed();

} catch (Throwable throwable) {

throw new RuntimeException(throwable);

}

}

@Override

public Object onFailure(String logStr) {

String onFailureMethodEL = lockMethod.failureExpression();

if (StringUtils.isEmpty(onFailureMethodEL)) {

return null;

}

Class<?> onFailureCallType = lockMethod.failureType();

if (onFailureCallType == void.class) {

return SPEL_EXPRESSION_PARSER.parseExpression(onFailureMethodEL).getValue();

} else {

EvaluationContext context = new StandardEvaluationContext(onFailureCallType);

Expression expression = SPEL_EXPRESSION_PARSER.parseExpression(onFailureMethodEL);

return expression.getValue(context);

}

}

});

}

private String parseLockName(Object[] args, LockMethod lockMethod, String[] parameterNames) {

ExtractParam[] extractParams = lockMethod.value();

if (extractParams.length == 0) {

throw new RuntimeException("not allow no extract param");

}

List<String> fieldValues = new ArrayList<>();

Map<String, Object> paramNameMap = buildParamMap(args, parameterNames);

for (ExtractParam extractParam : extractParams) {

String paramName = extractParam.paramName();

Object paramValue = paramNameMap.get(paramName);

String springEL = extractParam.fieldName();

String paramFieldValue = "";

if (StringUtils.isNotEmpty(springEL)) {

Expression expression = SPEL_EXPRESSION_PARSER.parseExpression(springEL);

paramFieldValue = expression.getValue(paramValue).toString();

} else {

if (isSimpleType(paramValue.getClass())) {

paramFieldValue = String.valueOf(paramValue);

}

}

fieldValues.add(paramFieldValue);

}

return String.format(lockMethod.formatter(), fieldValues.toArray());

}

/**

* 构建请求参数map

* @param args 参数列表

* @param parameterNames 参数名称列表

* @return key:参数名称 value:参数值

*/

private Map<String, Object> buildParamMap(Object[] args, String[] parameterNames) {

Map<String, Object> paramNameMap = new HashMap<>();

for (int i = 0; i < parameterNames.length; i++) {

paramNameMap.put(parameterNames[i], args[i]);

}

return paramNameMap;

}

/**

* 基本类型 int, double, float, long, short, boolean, byte, char, void.

*/

private static boolean isSimpleType(Class<?> clazz) {

return clazz.isPrimitive()

|| clazz.equals(Long.class)

|| clazz.equals(Integer.class)

|| clazz.equals(String.class)

|| clazz.equals(Double.class)

|| clazz.equals(Short.class)

|| clazz.equals(Byte.class)

|| clazz.equals(Character.class)

|| clazz.equals(Float.class)

|| clazz.equals(Boolean.class);

}

}

以上是 基于自定义注解和SpringEL表达式的分布式锁实现 的全部内容, 来源链接: utcz.com/z/517022.html

回到顶部