全局分布式锁实现

该代码示例展示了如何在Java中使用Redisson客户端实现分布式锁,并创建了一个自定义注解`GlobalRedisLock`来简化锁的使用。通过SpringAOP的环绕通知,实现了在方法执行前后自动获取和释放锁的功能,以保证并发控制的安全性。
摘要由CSDN通过智能技术生成
/**
 * 分布式锁工具类
 *
 * @author handafeng 2023/06/16 9:58
 **/
@Slf4j
@Component
public class GlobalRedisLockUtil {


    @Autowired
    private RedissonClient redissonClient;

    /**
     * 分布式锁
     *
     * @param lockKey
     * @param waittime 获取锁最大等待时长
     * @param timeout  锁自动释放时间
     * @return
     */
    public boolean tryLock(String lockKey, long waittime, long timeout) {

        try {
            RLock lock = redissonClient.getLock(lockKey);
            boolean getlock = lock.tryLock(waittime, timeout, TimeUnit.MILLISECONDS);
            log.info("获取锁" + Boolean.toString(getlock));
            return getlock;
        } catch (InterruptedException e) {
            e.printStackTrace();
            log.error("tryLockException", e.getMessage());
        }
        return false;
    }

    /**
     * 分布式锁
     *
     * @param lockKey
     * @param waittime 获取锁最大等待时长
     * @param timeout  锁自动释放时间
     * @return
     */
    public boolean tryLock(String lockKey, long waittime, long timeout, TimeUnit timeUnit) {

        try {
            RLock lock = redissonClient.getLock(lockKey);
            return lock.tryLock(waittime, timeout, timeUnit);
        } catch (InterruptedException e) {
            e.printStackTrace();
            log.error("tryLockException", e.getMessage());
        }
        return false;
    }

    /**
     * 手动释放锁
     *
     * @param lockKey
     * @return
     */
    public boolean unlock(String lockKey) {
        if (StringUtils.isBlank(lockKey)) {
            return false;
        }
        RLock lock = redissonClient.getLock(lockKey);
        if (lock.isLocked()) {
            lock.unlock();
        }
        return true;
    }

}
自定义注解:
/**
 * @author handafeng 2023/06/16 9:58
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface GlobalRedisLock {
    /**
     * 分布式缓存缓存key
     *
     * @return
     */
    String key() default "";

    /**
     * 获取锁最大等待时间
     *
     * @return
     */
    long waittime() default 100;

    /**
     * 自动释放锁,超时时间,默认3000毫秒
     *
     * @return
     */
    long timeout() default 3000;

    /**
     * 获取不到锁提示消息
     *
     * @return
     */
    String message() default "操作过快";

    /**
     * 是否锁定单用户操作,设置为true时,key属性不起作用
     *
     * @return
     */
    boolean isUserLock() default false;
}
环绕通知部分:
@Slf4j
@Aspect
@Component
public class GlobalRedisLockAspect {
    @Autowired
    private GlobalRedisLockUtil globalRedisLockUtil;

    private final String before2After = "analysisKey";

    @Pointcut("@annotation(cn.zybgs.handafeng.service.base.config.aspects.GlobalRedisLock)")
    public void globalControllerPointCut() {
    }

    @Before("globalControllerPointCut()")
    public void doBefore(JoinPoint joinPoint) {
        GlobalRedisLock redisLock = getAnnotationGlobalRedisLock(joinPoint);
        if (redisLock == null) {
            return;
        }
        ExpressRunner runner = new ExpressRunner();
        DefaultContext<String, Object> defaultContext = getRequestParamsByJoinPoint(joinPoint);
        String express = redisLock.key();
        String analysisKey = "";
        //是否为锁定用户频繁操作
        if (redisLock.isUserLock()) {
            analysisKey = getLockUserKey(joinPoint);
        } else {
            try {
                //解析EL表达式
                analysisKey = String.valueOf(runner.execute(express, defaultContext, null, true, false));
            } catch (Exception e) {
                e.printStackTrace();
                log.error("runner.executeException", e.getMessage());
            }
        }
        if (StringUtils.isBlank(analysisKey)) {
            return;
        }
        //把缓存key传递到after
        setAttributes2After(analysisKey);
        //获取分布式锁
        boolean locked = globalRedisLockUtil.tryLock(analysisKey, redisLock.waittime(), redisLock.timeout());
        if (!locked) {
            throw new ServiceException(101, redisLock.message());
        }
    }

    /**
     * 后置通知释放锁
     *
     * @param joinPoint
     */
    @After("globalControllerPointCut()")
    public void doAfter(JoinPoint joinPoint) {
        String rediskey = getLockKeyFromBefore();

        globalRedisLockUtil.unlock(rediskey);
    }

    /**
     * 把缓存key通过attributes传递到after
     *
     * @param analysisKey
     */
    private void setAttributes2After(String analysisKey) {
        HttpServletRequest request =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        request.setAttribute(before2After, analysisKey);
    }

    /**
     * 从attributes里面获取从before传输过来的数据
     *
     * @return
     */
    private String getLockKeyFromBefore() {
        HttpServletRequest request =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        return String.valueOf(request.getAttribute(before2After));
    }

    /**
     * 获取当前登录用户
     *
     * @param joinPoint
     * @return
     */
    private String getLockUserKey(JoinPoint joinPoint) {
        Long currentUserId = UserHolder.getUserId();
        if (currentUserId == null || currentUserId == 0) {
            return null;
        }
        return joinPoint.getSignature().getDeclaringTypeName().replace(".", ":") + ":" + joinPoint.getSignature().getName() + ":" + Long.toString(UserHolder.getUserId());
    }

    /**
     * 解析参数
     *
     * @param joinPoint
     * @return
     */
    private DefaultContext<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
        DefaultContext<String, Object> defaultContext = null;
        try {
            String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
            Object[] paramValues = joinPoint.getArgs();
            defaultContext = new DefaultContext<>();
            for (int i = 0; i < paramNames.length; i++) {
                defaultContext.put(paramNames[i], paramValues[i]);
                //region 服了老6,留着防止获取不到值时使用
                //判断是否基本类型,String需要单独排除
//                if (!ClassUtils.isPrimitiveOrWrapper(paramValues[i].getClass()) && !(paramValues[i] instanceof String)) {
//                    defaultContext.put(paramNames[i], JSONUtil.toBean(JSONUtil.toJsonStr(paramValues[i]), paramValues[i].getClass()));
//                } else {
//                defaultContext.put(paramNames[i], paramValues[i]);
//                }
                //endregion
            }
        } catch (Exception e) {
            log.error("getRequestParamsByJoinPointException", e.getMessage());
        }
        return defaultContext;
    }


    /**
     * 解析方法参数
     */
    private String generateKey(JoinPoint joinPoint, String spELString) {
        if (StringUtils.isBlank(spELString)) {
            return null;
        }
        // 通过joinPoint获取被注解方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        SpelExpressionParser parser = new SpelExpressionParser();
        //获取表达式
        Expression expression = parser.parseExpression(spELString);
        //设置解析上下文(有哪些占位符,以及每种占位符的值)
        EvaluationContext context = new StandardEvaluationContext();
        //获取参数值
        Object[] args = joinPoint.getArgs();
        //获取运行时参数的名称
        DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
        String[] parameterNames = discoverer.getParameterNames(method);
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        //解析,获取替换后的结果
        String result = Objects.isNull(expression.getValue(context)) ? "" : expression.getValue(context).toString();
        System.out.println(result);
        return result;
    }

    /**
     * 获取注解
     *
     * @param joinPoint
     * @return
     */
    private GlobalRedisLock getAnnotationGlobalRedisLock(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(GlobalRedisLock.class);
        }
        return null;
    }
}

实际使用示例:

@PostMapping("/testaspect")
@Operation(summary = "测试接口")
@GlobalRedisLock(key = "'key:ApiOrderController:testaspect:'+title+':'+name", message = "操作过快")
public JsonResult<Boolean> testaspect(@RequestParam("title") String title, @RequestParam("name") String name, Long s) {

    return JsonResult.ok(true);
}

@PostMapping("/testaspect1")
@Operation(summary = "测试接口1")
@GlobalRedisLock(key = "'key:ApiOrderController:workerRefundOrder1:'+excluder.orderId+':'+name", message = "操作过快")
public JsonResult<Boolean> testaspect1(@RequestBody OmsOrderExcluder excluder) {

    return JsonResult.ok(true);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值