1.新建注解接口
/**
* 方法加Redisson锁注解
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PreventConcurrent {
String key() default "";
}
2.编写切面类
package com.amarsoft.lease.annotation.aspect;
import com.amarsoft.lease.annotation.PreventConcurrent;
import group.rober.batch.core.BatchException;
import group.rober.runtime.kit.LogKit;
import group.rober.runtime.kit.StringKit;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Order(910)
public class PreventConcurrentAspect {
@Autowired
RedissonClient redissonClient;
//定义切点
@Pointcut("@annotation(preventConcurrent)")
public void preventConcurrentPointcut(PreventConcurrent preventConcurrent) {
}
@Before("preventConcurrentPointcut(preventConcurrent)")
public void before(JoinPoint joinPoint, PreventConcurrent preventConcurrent) {
String key = getKey(joinPoint, preventConcurrent);
RLock lock = redissonClient.getLock(key);
if (!lock.tryLock()) {
throw new BatchException("方法已被其他线程锁定,无法上锁[" + key + "]");
}
LogKit.info("上锁成功:{0}", key);
}
@After("preventConcurrentPointcut(preventConcurrent)")
public void after(JoinPoint joinPoint, PreventConcurrent preventConcurrent) {
String key = getKey(joinPoint, preventConcurrent);
RLock lock = redissonClient.getLock(key);
//判断是否已上锁并且为当前执行线程的锁
if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
LogKit.info("解锁成功:{0}", key);
};
}
//设置动态拼接key
private String getKey(JoinPoint joinPoint, PreventConcurrent preventConcurrent) {
String methodName = joinPoint.getSignature().getName();
String key = preventConcurrent.key();
if(StringKit.isBlank(key)){
return "PREVENT_CONCURRENT::" + methodName;
}
//创建ExpressionParser解SpEL析表达式
ExpressionParser parser = new SpelExpressionParser();
//SpEL表达式语法设置在parseExpression()入参内
Expression expression = parser.parseExpression(key);
EvaluationContext context = new StandardEvaluationContext();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Object[] args = joinPoint.getArgs();
DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
String[] paramNames = parameterNameDiscoverer.getParameterNames(methodSignature.getMethod());
for(int i = 0 ; i < args.length ; i++) {
context.setVariable(paramNames[i], args[i]);
}
Object result = expression.getValue(context);
String value = (result != null) ? result.toString() : null;
return "PREVENT_CONCURRENT::" + methodName + ":" + value;
}
}
3.该注解被调用时
@PreventConcurrent(key = "#queryBrandListDTO.brandName")
@RequestMapping(value = "/queryBrandList", method = RequestMethod.POST)
public ResultResponse<PaginationData<QueryBrandVO>> queryBrandList(@Valid @RequestBody QueryBrandListDTO queryBrandListDTO) {
PaginationData<QueryBrandVO> data = brandService.queryBrandList(queryBrandListDTO);
log.info("打印日志",data);
return ResultResponse.success("查询成功",data);
}
4.为切面框架加上缓存
package com.amarsoft.lease.annotation.aspect;
import com.amarsoft.lease.annotation.PreventConcurrent;
import group.rober.runtime.kit.LogKit;
import group.rober.runtime.kit.StringKit;
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.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@Aspect
@Component
@Order(910)
public class PreventConcurrentAspect {
/**
* 防并发锁键 前缀
*/
public static final String PREVENT_CONCURRENT_PREFIX = "PREVENT_CONCURRENT::";
/**
* 方法参数名称缓存
*/
public static final ConcurrentMap<Method,String[]> METHOD_PARAM_MAP = new ConcurrentHashMap<>();
/**
* 创建ExpressionParser解SpEL析表达式
*/
private static final ExpressionParser PARSER = new SpelExpressionParser();
private static final DefaultParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
@Autowired
RedissonClient redissonClient;
/**
*定义切点
*/
@Pointcut("@annotation(preventConcurrent)")
public void preventConcurrentPointcut(PreventConcurrent preventConcurrent) {
}
@Around("preventConcurrentPointcut(preventConcurrent)")
public Object before(ProceedingJoinPoint joinPoint, PreventConcurrent preventConcurrent) throws Throwable {
String key = getKey(joinPoint, preventConcurrent);
RLock lock = redissonClient.getLock(key);
try {
//直接上锁
lock.lock();
LogKit.info("上锁成功:{0}", key);
Object[] args = joinPoint.getArgs();
return joinPoint.proceed(args);
} finally {
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
LogKit.info("解锁成功:{0}", key);
}
}
}
/**
* <description> 设置动态拼接key </description>
* @param joinPoint :
* @param preventConcurrent :
* @return : java.lang.String
* @date : 2023/7/25 20:12
*/
private String getKey(ProceedingJoinPoint joinPoint, PreventConcurrent preventConcurrent) {
String lockName = preventConcurrent.value();
String methodName = joinPoint.getSignature().getName();
String key = preventConcurrent.key();
if(StringKit.isBlank(lockName)){
lockName = methodName;
}
if(StringKit.isBlank(key)){
return PREVENT_CONCURRENT_PREFIX + lockName;
}
//SpEL表达式语法设置在parseExpression()入参内
Expression expression = PARSER.parseExpression(key);
EvaluationContext context = new StandardEvaluationContext();
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
Object[] args = joinPoint.getArgs();
String[] paramNames = METHOD_PARAM_MAP.get(method);
if (paramNames == null) {
LogKit.info("解析方法参数名称");
paramNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
METHOD_PARAM_MAP.put(method, paramNames);
} else {
LogKit.info("从缓存中取出方法参数名称");
}
for(int i = 0 ; i < args.length ; i++) {
context.setVariable(paramNames[i], args[i]);
}
Object result = expression.getValue(context);
String value = (result != null) ? result.toString() : null;
return PREVENT_CONCURRENT_PREFIX + lockName + ":" + value;
}
}