接这这一篇redis分布式锁-java实现末尾,实现aop+自定义注解 实现分布式锁

1、为什么需要 声明式的分布式锁

编程式分布式锁每次实现都要单独实现,但业务量大功能复杂时,使用编程式分布式锁无疑是痛苦的,而声明式分布式锁不同,声明式分布式锁属于无侵入式,不会影响业务逻辑的实现。

我的为什么要用:使用简单,提升开发效率

2、怎么实现

使用spring aop + 自定义注解来实现

下面来看下spring boot + 自定义注解 如何实现一个声明式的分布式锁

3、看代码

第一步、引入aop 需要 jar包
        <!-- SpringBoot AOP start -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<!-- SpringBoot AOP end -->
第二步、@EnableAspectJAutoProxy 开启AOP
@EnableAspectJAutoProxy
@SpringBootApplication
public class DemoApplication { public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
} }
第三步:自定义DistributionLock注解
import java.lang.annotation.*;

/**
* 通常 和com.example.demo.annotation.DistributionLockParam 注解配合使用,降低锁粒度
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DistributionLock { /**
* 分布式锁key
*/
String key(); /**
* 尝试获得锁的时间(单位:毫秒),默认值:5000毫秒
*
* @return 锁key过期时间
*/
long tryLockTime() default 5000; /**
* 尝试获得锁后,持有锁的时间(单位:毫秒),默认值:60000毫秒
*
* @return
*/
long holdLockTime() default 60000; /**
* 分布式锁key 的分隔符(默认 :)
*/
String delimiter() default ":"; }
第四步:自定义DistributionLockParam注解
/**
* 分布式锁参数
* 这个注解,是给DistributionLock用来控制锁粒度的
*/
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DistributionLockParam {
}
第五步:定义分布式锁AOP配置第五步:定义分布式锁AOP配置
import com.example.demo.annotation.DistributionLock;
import com.example.demo.annotation.DistributionLockParam;
import com.example.demo.entity.Resp;
import com.example.demo.redisLock.LockParam;
import com.example.demo.redisLock.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List; /**
* 对springboot中aop切面编程的测试
*/
//切面类
@Aspect
@Component
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class RedisAopAspect {
public RedisAopAspect(){
log.info("分布锁 aop init");
}
/***
* 定义切入点
*/
@Pointcut("execution(@com.example.demo.annotation.DistributionLock * *(..))")
public void pointCut(){ } @Around(value = "pointCut()")
public Object aroundMethod(ProceedingJoinPoint pjp) {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod(); /////////////////////AOP 能取得的信息 start////////////////////////
// log.info("目标方法名为:{}",pjp.getSignature().getName());
// log.info("目标方法所属类的简单类名:{}" , pjp.getSignature().getDeclaringType().getSimpleName());
// log.info("目标方法所属类的类名:{}", pjp.getSignature().getDeclaringTypeName());
// log.info("目标方法声明类型:{}" , Modifier.toString(pjp.getSignature().getModifiers()));
// log.info("目标方法返回值类型:{}" , method.getReturnType());
// //获取传入目标方法的参数
// Object[] args = pjp.getArgs();
// for (int i = 0; i < args.length; i++) {
// log.info("第{}个参数为:{}" ,(i + 1) , args[i]);
// }
// log.info("被代理的对象:{}" , pjp.getTarget());
// log.info("代理对象自己:{}" , pjp.getThis());
/////////////////////AOP 能取得的信息 end//////////////////////// //取得注解对象数据
DistributionLock lock = method.getAnnotation(DistributionLock.class);
//分布式锁实际的key
String lockKey = getRealDistributionLockKey(pjp,lock);
//创建分布式锁对象 start
LockParam lockParam = new LockParam(lockKey,lock.tryLockTime(),lock.holdLockTime());
RedisLock redisLock = new RedisLock(lockParam);
//创建分布式锁对象 end //获取锁
Boolean holdLock = redisLock.lock();
log.info("lockKey:{} holdLock:{} ",lockKey,holdLock);
if(Boolean.FALSE.equals(holdLock)){
//获取锁失败后,处理返回结果
return handleAcquireLockFailReturn(pjp);
} try {
return pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}finally {
if(redisLock!=null){
Boolean unlock = redisLock.unlock();
log.info("释放锁:unlock {}",unlock);
}
}
} /**
* 分布式锁获取失败,处理方法
* @param pjp
* @return
*/
public Object handleAcquireLockFailReturn(ProceedingJoinPoint pjp){
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod();
Class returnType = method.getReturnType();
//通常每个公司都有自己的统一的返回对象,Resp.class可以根据自己现有的
if(returnType.equals(Resp.class) ){
log.info("返回值类型 Resp");
return Resp.buildFail("业务处理繁忙,请稍后重试");
} throw new RuntimeException("获取锁失败");
} /**
* 加了DistributionLockParam注解参数值,按照顺序返回list
* @param pjp
* @return
*/
public List<Object> getDistributionLockParamList(ProceedingJoinPoint pjp){
ArrayList<Object> distributionLockParamList = null;
MethodSignature signature = ((MethodSignature) pjp.getSignature());
//得到拦截的方法
Method method = signature.getMethod();
//获取方法参数注解,返回二维数组是因为某些参数可能存在多个注解
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
// log.info("parameterAnnotations:{}",parameterAnnotations);
//获取全部参数
Object[] objects = pjp.getArgs();
for(int i = 0; i < parameterAnnotations.length; i++){
for(Annotation a: parameterAnnotations[i]){
if(a.annotationType() == DistributionLockParam.class){
//初始化distributionLockParamList
if(distributionLockParamList==null){
distributionLockParamList = new ArrayList();
}
//获得参数值
Object o = objects[i];
distributionLockParamList.add(o);
}
}
} return distributionLockParamList;
} /**
* 加了DistributionLockParam注解参数值,拼接成字符串
* @param pjp
* @param lock
* @return
*/
public String getDistributionLockParamStr(ProceedingJoinPoint pjp,DistributionLock lock){
List<Object> distributionLockParamList = getDistributionLockParamList(pjp);
if(distributionLockParamList!=null && distributionLockParamList.size()>0){
StringBuffer sb = new StringBuffer();
for (int i = 0; i < distributionLockParamList.size(); i++) {
Object param = distributionLockParamList.get(i);
sb.append(lock.delimiter());
sb.append(param);
}
return sb.toString();
}
return "";
} /**
* 返回分布式锁key完整的key
* @param pjp
* @param lock
* @return
*/
public String getRealDistributionLockKey(ProceedingJoinPoint pjp,DistributionLock lock){
String distributionLockParamStr = getDistributionLockParamStr(pjp,lock);
return lock.key().concat(distributionLockParamStr);
} }

LockParam和RedisLock类已经在 【redis分布式锁-java实现】文章里面有,这里就不贴出来了


Service 使用例子
import com.example.demo.entity.Resp;

public interface IOrderService {
Resp updateOrder(String orderCode, Integer userId, Integer status);
}
import com.example.demo.annotation.DistributionLock;
import com.example.demo.annotation.DistributionLockParam;
import com.example.demo.entity.Resp;
import com.example.demo.service.IOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; @Slf4j
@Service
public class OrderServiceImpl implements IOrderService { @Override
@DistributionLock(key = "updateOrderSatus",tryLockTime = 1000)
public Resp updateOrder(@DistributionLockParam String orderCode, Integer userId, Integer status){
try {
log.info("updateOrder 处理业务 start");
Thread.sleep(1000*10);
log.info("updateOrder 处理业务 end");
} catch (InterruptedException e) {
e.printStackTrace();
}
return Resp.buildSuccess("修改订单状态成功");
} }
collection调用service
import com.example.demo.entity.Resp;
import com.example.demo.service.IOrderService;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; @Slf4j
@RestController
@RequestMapping(value = "/v1/test")
public class TestController {
@Autowired
IOrderService orderService; @ApiOperation(value = "修改订单状态")
@ApiImplicitParams({
@ApiImplicitParam(name = "orderCode", value = "订单编号", paramType = "query"),
@ApiImplicitParam(name = "userId", value = "用户ID", paramType = "query"),
@ApiImplicitParam(name = "status", value = "订单状态 1:未发货 2:已发货 3:完成", paramType = "query"),
})
@RequestMapping(value = "/updateOrderStatus", method = RequestMethod.PUT)
public Resp updateOrderStatus(@RequestParam(value = "orderCode")String orderCode,
@RequestParam(value = "userId")Integer userId,
@RequestParam(value = "status")Integer status){
log.info("updateOrderStatus reqParam:orderCode:{},userId:{},status:{}",orderCode,userId,status);
return orderService.updateOrder(orderCode,userId,status);
} }

4、浏览器swagger调用

5、下面调用一次这个接口,控制台打印的日志

Connected to the target VM, address: '127.0.0.1:63200', transport: 'socket'

  .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.0) 2021-05-25 23:13:10.967 INFO 57853 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2021-05-25 23:13:11.993 INFO 57853 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-05-25 23:13:12.000 INFO 57853 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-05-25 23:13:12.000 INFO 57853 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.46]
2021-05-25 23:13:12.066 INFO 57853 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-05-25 23:13:12.066 INFO 57853 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1059 ms
2021-05-25 23:13:12.119 INFO 57853 --- [ main] com.example.demo.config.RedisAopAspect : 分布锁 aop init
2021-05-25 23:13:12.694 INFO 57853 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-05-25 23:13:12.695 INFO 57853 --- [ main] d.s.w.p.DocumentationPluginsBootstrapper : Context refreshed
2021-05-25 23:13:12.705 INFO 57853 --- [ main] d.s.w.p.DocumentationPluginsBootstrapper : Found 1 custom documentation plugin(s)
2021-05-25 23:13:12.715 INFO 57853 --- [ main] s.d.s.w.s.ApiListingReferenceScanner : Scanning for api listing references
2021-05-25 23:13:12.880 INFO 57853 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 2.249 seconds (JVM running for 2.777)
2021-05-25 23:13:12.882 INFO 57853 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state LivenessState changed to CORRECT
2021-05-25 23:13:12.883 INFO 57853 --- [ main] o.s.b.a.ApplicationAvailabilityBean : Application availability state ReadinessState changed to ACCEPTING_TRAFFIC
2021-05-25 23:19:58.582 INFO 57853 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-05-25 23:19:58.582 INFO 57853 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2021-05-25 23:19:58.583 INFO 57853 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2021-05-25 23:20:11.970 INFO 57853 --- [nio-8080-exec-2] c.e.demo.controller.TestController : updateOrderStatus reqParam:orderCode:1,userId:2,status:3
2021-05-25 23:20:11.996 INFO 57853 --- [nio-8080-exec-2] com.example.demo.config.RedisAopAspect : lockKey:updateOrderSatus:1 holdLock:true
2021-05-25 23:20:12.000 INFO 57853 --- [nio-8080-exec-2] c.e.demo.service.impl.OrderServiceImpl : updateOrder 处理业务 start
2021-05-25 23:20:22.005 INFO 57853 --- [nio-8080-exec-2] c.e.demo.service.impl.OrderServiceImpl : updateOrder 处理业务 end
2021-05-25 23:20:22.007 INFO 57853 --- [nio-8080-exec-2] com.example.demo.config.RedisAopAspect : 释放锁:unlock true

spring boot启动类:com.example.demo.DemoApplication

swagger 地址:http://127.0.0.1:8080/swagger-ui.html

其他:

如果声明式的分布式锁想实现可重入机制,可以把new RedisLock(lockParam),替换成这篇文章的 redis分布式锁-可重入锁 就能实现了

代码我已经上传到gitee上了,大家可以下载玩一下,记得star一下

gitee 代码传送门

redis分布式锁-spring boot aop+自定义注解实现分布式锁的更多相关文章

  1. spring boot aop 自定义注解 实现 日志检验 权限过滤

    核心代码: package com.tran.demo.aspect; import java.lang.reflect.Method; import java.time.LocalDateTime; ...

  2. Spring Boot中自定义注解+AOP实现主备库切换

    摘要: 本篇文章的场景是做调度中心和监控中心时的需求,后端使用TDDL实现分表分库,需求:实现关键业务的查询监控,当用Mybatis查询数据时需要从主库切换到备库或者直接连到备库上查询,从而减小主库的 ...

  3. spring boot通过自定义注解和AOP拦截指定的请求

    一 准备工作 1.1 添加依赖 通过spring boot创建好工程后,添加如下依赖,不然工程中无法使用切面的注解,就无法对制定的方法进行拦截 <dependency> <group ...

  4. Spring Boot Web 自定义注解篇(注解很简单很好用)

    自从spring 4.0 开放以后,可以添加很多新特性的注解了.使用系统定义好的注解可以大大方便的提高开发的效率. 下面我贴一段代码来讲解注解: 通过小小的注解我们支持了以下功能: 使 spring. ...

  5. Spring Boot实现自定义注解

    在Spring Boot项目中可以使用AOP实现自定义注解,从而实现统一.侵入性小的自定义功能. 实现自定义注解的过程也比较简单,只需要3步,下面实现一个统一打印日志的自定义注解: 1. 引入AOP依 ...

  6. 使用AOP+自定义注解完成spring boot的接口权限校验

    记使用AOP+自定义注解完成接口的权限校验,代码如下: pom文件添加所需依赖: 1 <dependency> 2 <groupId>org.aspectj</group ...

  7. ssm+redis 如何更简洁的利用自定义注解+AOP实现redis缓存

    基于 ssm + maven + redis 使用自定义注解 利用aop基于AspectJ方式 实现redis缓存 如何能更简洁的利用aop实现redis缓存,话不多说,上demo 需求: 数据查询时 ...

  8. 利用Spring AOP自定义注解解决日志和签名校验

    转载:http://www.cnblogs.com/shipengzhi/articles/2716004.html 一.需解决的问题 部分API有签名参数(signature),Passport首先 ...

  9. ssm+redis整合(通过aop自定义注解方式)

    此方案借助aop自定义注解来创建redis缓存机制. 1.创建自定义注解类 package com.tp.soft.common.util; import java.lang.annotation.E ...

随机推荐

  1. Androidd Studio 之多行文字跑马灯特效

    •效果展示图 •参考资料 两种方法实现TextView跑马灯效果(字体横向滚动) •出现的问题 新建 Java 文件继承 TextView 时出现问题: •解决方法 不应该继承 $TextView$ ...

  2. Nginx常用部分命令

    Nginx一些命令 Windows 查看帮助信息 nginx -h 查看 nginx 版本 (小写字母 v) nginx -v 除版本信息外还显示配置参数信息 (大写字母 V) nginx -V 启动 ...

  3. [Fundamental of Power Electronics]-PART I-6.变换器电路-6.1 电路演化

    6.1 电路演化 第一章使用基本原理构建了buck变换器(图6.1).开关可以降低电压直流分量,低通滤波器可消除开关纹波.在CCM下,buck变换器的变换比为\(M=D\).buck变换器是最简单的, ...

  4. 梳理一下最近准备蓝桥杯时学习DP问题的想法

    学习时间不长,记录的只是学习过程的思路和想法,不能保证正确,代码可以在acwing上AC. 01背包问题: 1.首先是简单的01背包问题 2.先确定状态,f[i][j]表示有第i件物品,时间为j的最大 ...

  5. 【Prolog - 2.0 基础应用】

    [术语统一 terms unify] 两者统一,只需满足下面两条件之一 1.原本就是相同的 2.包含变量,这些变量可以用术语统一实例化,从而得到相等的术语 mia和mia是统一的,42和42是统一的, ...

  6. NDEBUG与assert

    当宏NDEBUG定义在assert的头文件之前,会使assert.trace这类调试函数失效, 需要注意的是#define NDEBUG必须放在这些函数的头文件之前,放在它们的 头文件后面的话就相当于 ...

  7. JVM学习笔记(一):JVM初探

    1 来源 来源:<Java虚拟机 JVM故障诊断与性能优化>--葛一鸣 章节:第一章 本文是第一章的一些笔记整理. 2 Java里程碑 2.1 Java起源 1990年Sun公司决定开发一 ...

  8. Ambassador-07-熔断

    Ambassador的熔断机制的定义 circuit_breakers: - priority: <string> max_connections: <integer> max ...

  9. etcd简介及集群安装部署使用

    目录 1. 简介 2. Linux下载安装 3. 单机模式启动 4. 指定各集群成员的方式配置集群 5. 使用discovery service的方式配置集群 6. 集群模式下客户端命令行 7. et ...

  10. Django 视图(View)

    1. 视图简介 2. URLconf 1)关联各应用下的 URLconf 2)URLconf 的编写 3)namespace 反向解析 3. 视图函数&错误视图 4. HttpRequest ...