Spring的表达式解析器+AOP+Redis实现注解分布式锁

7 篇文章 0 订阅
7 篇文章 0 订阅

使用Spring的表达式解析器+Redis实现分布式锁

怎么配置使用Redis就不在这里说明了!!!

AOP依赖

		<!--引用AOP依赖-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

1.0 定义分布式锁注解

/**
 * @Author: ZhiHao
 * @Date: 2022/7/28 20:19
 * @Description: 分布式锁注解
 * @Versions 1.0
 **/
@Documented
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {

    /**
     * 锁前缀
     *
     * @return
     */
    String lockKeyPrefix() default "";

    /**
     * 为空默认以用户维度加锁
     * 锁Key参数解析表达式 (自定义实体是 User user => #user.id 、 
     * |  Map<String, String> params   => #params['phone']
     * |  string、int    params   => #params
     * |  多个参数   => 'id_'+#user.id+'age_'+#user.age
     *
     * @return
     */
    String lockKeyValue();

    /**
     * 锁住时间 (秒)
     * @return
     */
    double lockTime() default 1.5;

    /**
     * 加锁失败抛异常的提示
     * @return
     */
    LockFailTips lockFailTips() default LockFailTips.DEFAULT_VALUE;
    
      /**
     * 添加用户 masterId_12345  标识
     * @return
     */
    boolean addLockKeyUserFlag() default false;
}

2.0 失败抛异常提示枚举

public enum LockFailTips {

    ORDER_SUBMIT(()->{throw  new RuntimeException("订单提交点击太快了!");},"订单信息提交"),
    
    DEFAULT_VALUE(()->{throw  new RuntimeException("加分布式锁失败!");},"默认提示"),
    ;
    private Supplier<?> supplier;
    private String describe;

    LockFailTips(Supplier<?> supplier, String describe) {
        this.supplier = supplier;
        this.describe = describe;
    }

    public Supplier<?> getSupplier() {
        return supplier;
    }
}

3.0 注解式AOP实现

/**
 * @Author: ZhiHao
 * @Date: 2022/7/28 20:07
 * @Description:
 * @Versions 1.0
 **/
@Aspect
@Service
@Slf4j
public class RedisDistributedLock implements Ordered {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @Autowired
    private HttpServletRequest request;

    private static final String PROJECT_NAME = "order-api:";

    private static final String LOCK_SCRIPT = "if redis.call('setnx', KEYS[1], ARGV[1] ) == 1 then return redis.call('pexpire', KEYS[1], tonumber(ARGV[2])) else return 0 end";

    private static final String RELEASE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    private static final DefaultRedisScript<Long> lock_script;
    private static final DefaultRedisScript<Long> release_lock_script;

    // 表达式解析器
    private static final ExpressionParser expressionParser = new SpelExpressionParser();
    // 类方法参数命名获取, 底层是通过读取文件解析获取到的
    private static final LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer
            = new LocalVariableTableParameterNameDiscoverer();

    static {
        lock_script = new DefaultRedisScript<>();
        lock_script.setScriptText(LOCK_SCRIPT);
        lock_script.setResultType(Long.class);

        release_lock_script = new DefaultRedisScript<>();
        release_lock_script.setScriptText(RELEASE_LOCK_SCRIPT);
        release_lock_script.setResultType(Long.class);
    }

    @Pointcut("@annotation(com.zhihao.annotation.DistributedLock)")
    private void expression() {
    }

    /**
     * 使用环谣通知
     * @Before("expression()") => @Before("@annotation(distributedLock)") 可以直接方法参数定义使用注解
     * @param joinPoint
     * @param distributedLock
     * @author: ZhiHao
     * @date: 2022/7/28
     * @return
     */
    @Around("@annotation(distributedLock)")
    public Object lock(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {
        // 锁的值
        String value = UUID.randomUUID().toString();
        // 获取锁的key
        String lockKey = this.getLockKey(joinPoint,distributedLock);
        if (log.isDebugEnabled()){
            log.debug("distributedLock===requestUrl:{}, lockKey:{}, requestId:{}",request.getServletPath(),lockKey,requestId);
        }
        try {
            // 加锁
            Boolean lock = this.distributedLock(lockKey,value, distributedLock);
            if (!lock){
                // 加锁失败抛出异常
                distributedLock.lockFailTips().getSupplier().get();
            }
            // 执行被切面方法
            return joinPoint.proceed(joinPoint.getArgs());
        } finally {
            // 解锁
            redisTemplate.execute(release_lock_script, Collections.singletonList(lockKey), value);
        }
    }

    /**
     * 加锁
     *
     * @param lockKey
     * @param value
     * @param distributedLock
     * @return java.lang.Boolean true=成功
     * @author: ZhiHao
     * @date: 2022/7/28
     */
    private Boolean distributedLock(String lockKey, String value, DistributedLock distributedLock) {
        double lockTime = distributedLock.lockTime() * 1000;
        Long result = redisTemplate.execute(lock_script, Collections.singletonList(lockKey), value, String.valueOf(lockTime));
        return Optional.ofNullable(result).orElse(0L).equals(1L);
    }

    /**
     * 获取Key
     *
     * @param joinPoint
     * @param distributedLock
     * @return java.lang.String
     * @author: ZhiHao
     * @date: 2022/7/28
     */
    private String getLockKey(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
        StringBuilder keyBuilder = new StringBuilder(PROJECT_NAME);
        // 获取注解数据
        String keyPrefix = distributedLock.lockKeyPrefix();
        String keyValue = distributedLock.lockKeyValue();

        keyBuilder.append(keyPrefix);

        // 获取方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取参数
        Object[] args = joinPoint.getArgs();
        // 获取方法
        Method targetMethod = methodSignature.getMethod();
        // 获取方法参数名称
        String[] parameterNames = parameterNameDiscoverer.getParameterNames(targetMethod);
    
        // 如果表达式为空, 或者表达式不为空, 参数为空, 使用方法名称 作为Key
        Integer length = Optional.ofNullable(args).map(el -> el.length).orElse(0);
        if (StrUtil.isBlank(keyValue) || (StrUtil.isNotBlank(keyValue) && length <= 0)) {
            return keyBuilder.append(keyValue).append(targetMethod.getName()).toString().trim();
        }
        
        // 封装表达式变量和参数
        StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
        for (int i = 0; i < args.length; i++) {
            evaluationContext.setVariable(parameterNames[i], args[i]);
        }
        // 解析表达式
        Expression expression = expressionParser.parseExpression(keyValue);
        String value = expression.getValue(evaluationContext, String.class);
        // 拼接最终key返回
        return keyBuilder.append(value).toString();
    }
    
    // AOP优先级最高
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE+1;
    }
}

4.0 使用

@PostMapping("/post")
// 取user里面的age属性setVariable(user, user)
@DistributedLock(lockKeyPrefix = "order_submit:",lockKeyValue = "#user.age",   
        lockFailTips = LockFailTips.ORDER_SUBMIT,LockTime = 5.5)
public User post(@RequestBody @Valid User user) {
    System.out.println(user);
    return user;
}

//-------------------------
@GetMapping("/gets")
// 取name属性setVariable(name, args[i])
@DistributedLock(lockKeyPrefix = "order_submitsss:",lockKeyValue = "#name",
                 lockFailTips = LockFailTips.ORDER_SUBMIT,LockTime = 5.5)
public User gets(String name) {

}

//-------------多个参数Map--------------
@DistributedLock(lockKeyPrefix = "superTeamManage_lockKey:memberInvite-", addLockKeyUserFlag = true,
            lockKeyValue = "'phone_'+#params['phone']+'age_'+#params['age']")
    @PostMapping("/memberInvite")
    public BaseDialog memberInvite(@RequestBody @NotEmpty(message = "参数不能为空!") Map<String, String> params) {
        return superTeamManageService.memberInvite(params.get("phone"));
    }

//-------------多个参数自定义实体--------------
@DistributedLock(lockKeyPrefix = "order_submit:",lockKeyValue = "'age_'+#user.age+'name_'+#user.name",   
        lockFailTips = LockFailTips.ORDER_SUBMIT,LockTime = 5.5)
public User post(@RequestBody @Valid User user) {
    System.out.println(user);
    return user;
}

如果获取的参数值为null, 表达式解析器解析出来的结果是: phone_null, 可用MVC搭配使用参数验证参数绑定, 先校验参数为空抛出异常!

扩展:

parameternamediscoverer 解析

Spring的表达式解析器模式(学习笔记) 自行搜索

StandardEvaluationContext 优化

StandardEvaluationContext 可以更换使用 MethodBasedEvaluationContext

private static final LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer
            = new LocalVariableTableParameterNameDiscoverer();
// 封装表达式变量和参数
ExpressionRootObject rootObject = new ExpressionRootObject(joinPoint.getTarget(),args);
MethodBasedEvaluationContext evaluationContext = new MethodBasedEvaluationContext(rootObject ,targetMethod,args,parameterNameDiscoverer);

// 解析表达式
Expression expression = expressionParser.parseExpression(keyValue);
String value = expression.getValue(evaluationContext, String.class);


// -----------------------ExpressionRootObject--------------------------
@Data
public class ExpressionRootObject {
    private final Object object;

    private final Object[] args;

    public ExpressionRootObject(Object object, Object[] args) {
        this.object = object;
        this.args = args;
    }
}

优化: 缓存表达式Expression

自定义ExpressionEvaluator 继承 CachedExpressionEvaluator

@Component
public class ExpressionEvaluator extends CachedExpressionEvaluator {

    private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(64);

    public Object key(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext) {
        // 调用父类实现的缓存表达式
        return getExpression(this.conditionCache, elementKey, conditionExpression).getValue(evalContext);
    }
}

注入使用

@Autowired
private ExpressionEvaluator evaluator;

private String getLockKey(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
        StringBuilder keyBuilder = new StringBuilder(PROJECT_NAME);
        // 获取注解数据
        String keyPrefix = distributedLock.lockKeyPrefix();
        String keyValue = distributedLock.lockKeyValue();

        keyBuilder.append(keyPrefix);

        // 添加用户标识
        if (distributedLock.addLockKeyUserFlag()) {
            keyBuilder.append("masterId_").append(通过request获取).append("_");
        }

        // 获取方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取参数
        Object[] args = joinPoint.getArgs();
        // 获取方法
        Method targetMethod = methodSignature.getMethod();
        // 获取被切目标对象
        Object target = joinPoint.getTarget();

        // 如果表达式为空, 或者表达式不为空, 参数为空, 使用方法名称+用户标识, 作为Key
        Integer length = Optional.ofNullable(args).map(el -> el.length).orElse(0);
        if (StrUtil.isBlank(keyValue) || (StrUtil.isNotBlank(keyValue) && length <= 0)) {
            return keyBuilder.append(keyValue).append(targetMethod.getName())
                    .append("masterId_").append(通过request获取).toString().trim();
        }

        // 封装表达式变量和参数
        ExpressionRootObject rootObject = new ExpressionRootObject(target,args);
        MethodBasedEvaluationContext evaluationContext = new MethodBasedEvaluationContext(rootObject ,targetMethod,args,parameterNameDiscoverer);

        // 解析表达式
        AnnotatedElementKey methodKey = new AnnotatedElementKey(targetMethod, target.getClass());
        Object value = evaluator.key(keyValue, methodKey, evaluationContext);
        // 拼接最终key返回
        return keyBuilder.append(value).toString();
    }

1

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懵懵懂懂程序员

如果节省了你的时间, 请鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值