前言

此文档只粗略的讲解实现思路,具体的实现逻辑还需要针对业务区别处理。

需求

因为此业务中有读和写的操作,写的执行条件依赖于读,并发条件下可能出现读到相同的条件均可以去执行写操作,此时写就会出现脏数据,。所以项目需要实现,在处理业务时,加锁防止并发问题,此处利用Redis实现,但是如果多个业务都需要这么操作的话,其实操作Redis的代码是相同的,这样就显得麻烦,所以楼主采用注解的形式实现,具体方法见下述。

实现逻辑

在请求调用Controller层时,RequestMapping 映射到的方法上加上注解,如自定义注解 @Debounce(防止多次提交)。

此时需要考虑几个问题

1、利用Redis实现并发锁的操作对Redis来说实际上就是一种Key的操作,那么自定义注解@Debounce如何实现key的自定义且根据参数可变化?

2、如何实现调用请求真实的处理方法时的拦截?

3、什么情况下才会去做这个事情?

针对第一个问题解决方法

利用处理请求的方法中的参数,实现动态定义,此时又有个问题,就是说如果时基本数据类型+String,这样的可以直接将值获取拼接,但是如果参数中有对象的话,同时又想用对象中的属性作为key值的一部分,那么直接拼接就行不通。像这种情况,统一的方式行不通,那么自然而然就会想到此处必须用到了拓展类,在上层只是定义这种功能,具体的实现由子类负责具体实现。(详见后述)。

在@Debounce注解中有定义一个处理参数数组,值为处理请求的方法中的参数位置Num,从0开始依次递增,同时也有个处理类class,作用是具体实现key值的拼接。

针对第二个问题解决方法

当然是利用代理实现,此处利用的是Spring的Cglib动态代理。同时利用Spring开放的拓展Bean处理的接口BeanPostProcessor,在bean实例化后,实例化Cglib代理。

针对第三个问题解决方法

在Controller层即在有注解@Controller 或者 @RestController 的类中才会去判断是否需要做此操作。

具体实现方法

Debounce 注解

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Debounce {

/**

* 使用的锁键值

*/

String lockKey() default "";

/**

* 使用方法的参数(toString)做为锁的KEY使用

* 规则:从0开始计,0表示方法中的第一个参数,以此类推

* 和 lockKey 都为空时,使用方法名进行锁定

*/

int[] methodParameters() default {};

/**

* 对注释中的参数进行修改,默认为字符串拼接

*

* @return

*/

Class extends MethodParametersHandler> handler() default MethodParametersHandler.class;

/**

* 延时关闭,当调用的方法结束后并不关闭锁,只有真正超时后才关闭

* 即在锁定时间内,只允许被调用一次

*

* @return

*/

boolean delayClose() default false;

/**

* 默认的超时时间,这个时间内除非原来的方法调用结束,否则无法点击

* 如果原来的方法已经结束,时间比这个短,那么这时间无效

*/

@AliasFor("lockExpireEsc")

int value() default 5;

/**

* 锁的超时时间

*

* @return

*/

@AliasFor("value")

int lockExpireEsc() default 5;

}

参数处理接口 MethodParametersHandler

此处做参数参数值的拼接同时返回拼接后的数据

public interface MethodParametersHandler {

String handler(Object[] args) throws IllegalAccessException;

static class Default implements MethodParametersHandler {

@Override

public String handler(Object[] args) {

StringBuilder sb = new StringBuilder();

for (Object arg : args) {

if (arg != null) {

sb.append(String.valueOf(arg));

sb.append("#");

}

}

return sb.toString();

}

}

}

方法拦截器定义 DebounceInvocationHandler

public class DebounceInvocationHandler implements MethodInterceptor {

private Map, MethodParametersHandler> methodParametersHandlerMap = new ConcurrentHashMap<>();

private final Object target;

private static final MethodParametersHandler methodParametersHandler = new MethodParametersHandler.Default();

public DebounceInvocationHandler(Object bean) {

this.target = bean;

}

@Override

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

Debounce annotation = method.getAnnotation(Debounce.class);

if (annotation != null) {

int value = (int) AnnotationUtils.getValue(annotation);

if (value <= 0) {

value = 10;

}

// 组装Redis的key

String key = annotation.lockKey();

int[] methodParameters = annotation.methodParameters();

if (methodParameters != null && methodParameters.length > 0) {

Object[] handlerArgs = new Object[methodParameters.length];

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

if (methodParameters[i] < args.length) {

handlerArgs[i] = args[methodParameters[i]];

}

}

MethodParametersHandler parametersHandler = null;

Class extends MethodParametersHandler> handler = annotation.handler();

if (handler == MethodParametersHandler.class) {

parametersHandler = methodParametersHandler;

} else {

if (methodParametersHandlerMap.containsKey(handler)) {

parametersHandler = methodParametersHandlerMap.get(handler);

} else {

MethodParametersHandler instance = handler.newInstance();

parametersHandler = methodParametersHandlerMap.putIfAbsent(handler, instance);

}

}

key += parametersHandler.handler(handlerArgs);

}

if (StringUtils.isEmpty(key)) {

key = method.toString();

}

// Redis 的分布式锁实现,代码省略 , 不满足锁的条件可以直接返回或是抛异常

}

try {

if (target == null) {

return methodProxy.invokeSuper(proxy, args);

} else {

return methodProxy.invoke(target, args);

}

} finally {

// 释放Reids 锁判断

if (annotation != null && (Redis 锁不为空) && !annotation.delayClose()) {

// 释放Redis锁

}

}

}

}

Bean实例化后的实现方式-BeanPostProcessor

@Configuration

public class RestfulMVCAutoConfiguration implements BeanPostProcessor {

@Override

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

return bean;

}

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

Class> beanClass = bean.getClass();

RestController annotation = beanClass.getAnnotation(RestController.class);

if (annotation == null) {

return bean;

}

boolean haveDebounce = false;

Method[] methods = beanClass.getDeclaredMethods();

for (Method method : methods) {

Debounce debounce = method.getAnnotation(Debounce.class);

if (debounce != null) {

haveDebounce = true;

break;

}

}

if (haveDebounce) {

Enhancer en = new Enhancer();

en.setSuperclass(beanClass);

en.setUseFactory(false);

en.setCallback(new DebounceInvocationHandler(bean));

return en.create();

}

return bean;

}

}

使用方式

其中的MyHandler.class 为 implements MethodParametersHandler ,参数组装的具体实现

@RestController

@RequestMapping("/test/debounce/")

public class DebounceController {

@PostMapping(value = "/post")

@Debounce(value = 10, handler = MyHandler.class , delayClose = true, methodParameters = 0)

public TResponseObject post(@RequestBody MyRequest request) {

return TResponseObject.Success("Success");

}

}

getHandel redis_Controller层利用Redis实现分布式锁(注解实现)相关推荐

  1. 【Redis】利用 Redis 实现分布式锁

    技术背景 首先我们需要先来了解下什么是分布式锁,以及为什么需要分布式锁. 对于这个问题,我们可以简单将锁分为两种--内存级锁以及分布式锁,内存级锁即我们在 Java 中的 synchronized 关 ...

  2. 利用redis实现分布式锁

    一.背景 在分布式项目中,由于一个服务会有多个实例运行,有些特定的场景需要我们用到分布式锁. 例如:最近我正在做的交易所项目,其中一个服务是钱包模块,需要每半个小时就去归集用户的资金,这个定时任务只能 ...

  3. 利用redis实现分布式锁:加锁与解锁

    待补充 转载于:https://www.cnblogs.com/csuliujia/p/10451462.html

  4. Redis实战——分布式锁

    目录 1 一人一单并发安全问题 2 分布式锁的原理和实现 2.1 什么是分布式锁? 2.2 分布式锁的实现 1 一人一单并发安全问题 之前一人一单的业务使用的悲观锁,在分布式系统下,是无法生效的. 理 ...

  5. nx set 怎么实现的原子性_基于Redis的分布式锁实现

    前言 本篇文章主要介绍基于Redis的分布式锁实现到底是怎么一回事,其中参考了许多大佬写的文章,算是对分布式锁做一个总结 分布式锁概览 在多线程的环境下,为了保证一个代码块在同一时间只能由一个线程访问 ...

  6. 基于Redis的分布式锁实现

    本文转自 一.分布式锁概览 在多线程的环境下,为了保证一个代码块在同一时间只能由一个线程访问,Java中我们一般可以使用synchronized语法和ReetrantLock去保证,这实际上是本地锁的 ...

  7. Go + Redis 实现分布式锁

    文章目录 一.前言 1.1 需要对交易订单加锁原因 1.2 加锁方案 二.Go + Redis 实现分布式锁 2.1 为什么需要分布式锁 2.2 分布式锁需要具备特性 2.3 实现 Redis 锁应先 ...

  8. redisson的锁的类型_绝对干货:利用redisson完成分布式锁功能

    在单体架构中,我们使用synchronize或者Lock就能完成上锁同步的操作,但是这些在分布式,微服务的今天,失去了作用. 分布式锁的实现一般有三种解决方案:基于数据库表实现 基于缓存实现,比如re ...

  9. Zookeeper和Redis实现分布式锁,附我的可靠性分析

    作者:今天你敲代码了吗 链接:https://www.jianshu.com/p/b6953745e341 在分布式系统中,为保证同一时间只有一个客户端可以对共享资源进行操作,需要对共享资源加锁来实现 ...

最新文章

  1. linux下xampp(apache)中配置域名访问,以及遇到的问题
  2. 深度学习笔记 第四门课 卷积神经网络 第二周 深度卷积网络:实例探究
  3. 对应生成树的基本回路_7.1 图的定义与基本术语
  4. 微信小程序与Vue js数据渲染对比
  5. ajax简单做html查询删除(鲜花)
  6. 【算法学习笔记】81.动态规划 分类讨论 SJTU OJ 1075 括号匹配升级
  7. xml property标签注入一个类变量_依赖注入的学习
  8. 斐讯K2P B1 博通TTL刷机方法
  9. 什么是领导能力?如何提高领导能力?
  10. Halcon深度学习目标检测例程学习经验(1)
  11. SAP ABAP 取物料号的采购订单文本内容
  12. WEB学习路线2020完整版+附视频教程
  13. Vue一级路由与二级路由/路由重定向
  14. 振弦传感器不同线制分类
  15. 智慧渣土运输管控系统
  16. 项目 3: 创建用户分类
  17. 下载3D元件模型导入Altium Designer并制作PCB元件库
  18. DIT-FFT[C语言实现]
  19. 堆积木(动态数组vector)
  20. codeMirror 使用教程

热门文章

  1. 为什么技术人干得越久越拿不到高薪?
  2. SQL用了两年多,分享2个最常用的小技巧
  3. 快速掌握 JavaScript 库必备的 5 大技巧!
  4. 新功能又来啦!这次是「代码搜索」和视频直播!
  5. 年轻一代 winner 的程序人生,改变世界的起点藏在身边
  6. 未来的程序员都将在浏览器中编码!
  7. 亚马逊机器学习工程师面试怎么过?
  8. “手把手撕LeetCode题目,扒各种算法套路的裤子”
  9. 另类数据解读 : 口罩是什么时候成为硬通货的?!
  10. Android 设备上可以实现 3D Touch 吗?| 原力计划