/** * 分布式锁工具类 * * @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); }