网站首页 > 博客文章 正文
1. 自定义注解实现分布式锁
利用自定义注解实现分布式锁,最麻烦的地方就是,加锁的key,怎么获取,之前项目中,对key的处理是:在调用需要加分布式锁方法前,就把key拼装完毕,然后在需要加锁的方法的第一个参数传入key,然后在切面类里面通过反射的方式拿到这个key,执行锁获取。
这种方式对方法的侵入比较大,需要函数多出一个参数,传入key,而在方法内部并没有使用到这个key,看着很不舒服。所以决定通过spel解决这个问题,spel就是 Spring表达式语言全称为“Spring Expression Language”,缩写为“SpEL”,具体的可以搜索下,网上相关的博文很多。
2. SpelUtil
在spring 的基础之上封装的用于解析spel表达式的工具方法
import org.springframework.context.expression.MethodBasedEvaluationContext; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import java.lang.reflect.Method; /** * 解析SPEL 表达式 * @author huxingnan * @date 2018/5/21 10:51 */ public class SpelUtil { public static String parse(String spel, Method method, Object[] args) { //获取被拦截方法参数名列表(使用Spring支持类库) LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer(); String[] paraNameArr = u.getParameterNames(method); //使用SPEL进行key的解析 ExpressionParser parser = new SpelExpressionParser(); //SPEL上下文 StandardEvaluationContext context = new StandardEvaluationContext(); //把方法参数放入SPEL上下文中 for (int i = 0; i < paraNameArr.length; i++) { context.setVariable(paraNameArr[i], args[i]); } return parser.parseExpression(spel).getValue(context, String.class); } /** * 支持 #p0 参数索引的表达式解析 * @param rootObject 根对象,method 所在的对象 * @param spel 表达式 * @param method ,目标方法 * @param args 方法入参 * @return 解析后的字符串 */ public static String parse(Object rootObject,String spel, Method method, Object[] args) { //获取被拦截方法参数名列表(使用Spring支持类库) LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer(); String[] paraNameArr = u.getParameterNames(method); //使用SPEL进行key的解析 ExpressionParser parser = new SpelExpressionParser(); //SPEL上下文 StandardEvaluationContext context = new MethodBasedEvaluationContext(rootObject,method,args,u); //把方法参数放入SPEL上下文中 for (int i = 0; i < paraNameArr.length; i++) { context.setVariable(paraNameArr[i], args[i]); } return parser.parseExpression(spel).getValue(context, String.class); } }
3. 自定义注解addLock
import java.lang.annotation.*; /** * 自定义注解为了加锁使用 * Created by zhaosh on 2017/5/12. */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface AddLock { //spel表达式 String spel() ; //log信息 String logInfo() default ""; }
4. AOP 切面类AddLockAspect
package com.dream.aspect; import com.dream.annotation.AddLock; import com.dream.service.AddLockService; import com.dream.util.SpelUtil; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicBoolean; /** * @author hu */ @Aspect @Component public class AddLockAspect { private Logger logger = LoggerFactory.getLogger(getClass()); @Resource private AddLockService addLockService; @Pointcut("@annotation(com.dream.annotation.AddLock)") public void addLockAnnotationPointcut() { } @Around(value = "addLockAnnotationPointcut()") public Object addKeyMethod(ProceedingJoinPoint joinPoint) throws Throwable { //定义返回值 Object proceed; //获取方法名称 String logInfo = getLogInfo(joinPoint); //前置方法 开始 String redisKey = getRediskey(joinPoint); logger.info("{}添加redisKey={}",logInfo,redisKey); AtomicBoolean lockState = new AtomicBoolean(false); try { //对key加分布式锁:1表示加锁成功,-1表示存在锁且未过期,-2表示锁过期但被其它进程抢先 int addLock = addLockService.addLock(redisKey, 5); if (addLock <=0 ) { throw new RuntimeException("加锁失败:锁存在"); } logger.info("{},设置redisKey成功,key={}",logInfo, redisKey); lockState.set(true); // 目标方法执行 proceed = joinPoint.proceed(); } catch (Exception exception) { logger.error("{}REDIS加锁失败,key = {},message={}, code = {}", logInfo,redisKey, exception.getMessage(), exception); throw exception; } finally { if (lockState.get()) { logger.info("{}清除redisKey={}",logInfo,redisKey); addLockService.clearLock(redisKey); } } return proceed; } /** * 获取 指定 loginfo * 需要接口方法声明处 添加 AddLock 注解 * 并且 需要填写 loginfo * @param joinPoint 切入点 * @return logInfo */ private String getLogInfo(ProceedingJoinPoint joinPoint){ MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature(); Method method = methodSignature.getMethod(); AddLock annotation = AnnotationUtils.findAnnotation(method, AddLock.class); if(annotation == null){ return methodSignature.getName(); } return annotation.logInfo(); } /** * 获取拦截到的请求方法 * @param joinPoint 切点 * @return redisKey */ private String getRediskey(ProceedingJoinPoint joinPoint) { Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method targetMethod = methodSignature.getMethod(); Object target = joinPoint.getTarget(); Object[] arguments = joinPoint.getArgs(); AddLock annotation = AnnotationUtils.findAnnotation(targetMethod, AddLock.class); String spel=null; if(annotation != null){ spel = annotation.spel(); } return SpelUtil.parse(target,spel, targetMethod, arguments); } }
5. AddLockService
具体实现就不贴出来啦
/** * @author huxingnan * @date 2018/5/21 13:33 */ public interface AddLockService { /** * 加锁 * @param redisKey key * @param i i秒之后失效 * @return 1 成功 -1 失败 -2 失败 */ int addLock(String redisKey, int i); /** * 清除锁 * @param redisKey key */ void clearLock(String redisKey); }
6. 使用AddLock
示范1:
@AddLock(spel = "'CreateOutOrder'+#p0.orderCode",logInfo = "日志信息") public void testAddLock(Order order){ }
示范2:
@AddLock(spel = "'CreateOutOrder'+#order.orderCode",logInfo = "日志信息") public void testAddLock(Order order){ }
猜你喜欢
- 2024-09-11 Spring Security 全局方法安全:预过滤和后过滤(2)
- 2024-09-11 「Spring Boot 源码研究 」- 自动化装配条件化配置Conditional剖析
- 2024-09-11 面试:Spring Boot 中的条件注解底层是如何实现的?
- 2024-09-11 SpringBoot系列(十五)整合缓存,项目必用的技术
- 2024-09-11 如何优雅地记录操作日志?(如何优雅地记录操作日志)
- 2024-09-11 SpEL应用实战(应用spc技术)
- 2024-09-11 有趣的SpEL注入(有趣的工作群名称大全)
- 2024-09-11 SpringBoot 实现异步记录复杂日志
- 2024-09-11 redis 分布式锁(redis 分布式锁失效时间)
- 2024-09-11 CVE-2022-22947分析(cve-2020-0796分析)
你 发表评论:
欢迎- 06-23MySQL合集-mysql5.7及mysql8的一些特性
- 06-23MySQL CREATE TABLE 简单设计模板交流
- 06-23MYSQL表设计规范(mysql设计表注意事项)
- 06-23MySQL数据库入门(四)数据类型简介
- 06-23数据丢失?别慌!MySQL备份恢复攻略
- 06-23MySQL设计规范(mysql 设计)
- 06-23MySQL数据实时增量同步到Elasticsearch
- 06-23MySQL 避坑指南之隐式数据类型转换
- 最近发表
- 标签列表
-
- powershellfor (55)
- messagesource (56)
- aspose.pdf破解版 (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- vue数组concat (56)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)