笔者在新公司也需要用到分布式加锁, 由于redis 版本较低的问题, 选型的时候还是选了自己在上家公司的实现方式(分布式加锁)的最后一种
但是这次笔者进行了优化, 用自定义注解+aop的方式来结合, 这样最大限度减少了调用者的开发量.
不多说, 直接上代码:
自定义注解:
这里加锁的key需要用到两个字段来区分不同业务, 所以定义了fieldOne和fieldTwo
* 使用方式: 1.直接把该注解加在方法上
* 2.如果方法入参是对象形式, 请选择对象中的两个字段名赋值给fieldOne和fieldTwo,若对象中已经包含registNo和taskId中的一个或者两个, 则包含的不用赋值
* 3.如果方法入参是非对象形式, 而是多个单独参数, 则选择其中两个参数名称赋值给fieldOne和fieldTwo
* 4.其它情况暂不支持
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD)//作用范围,方法上 @Retention(RetentionPolicy.RUNTIME)//运行时 @Documented public @interface GetLock { String fieldOne() default "registNo"; //报案号 String fieldTwo() default "taskId"; //任务Id }
AOP切面
这里是用对象中的两个参数或者方法中的两个参数来进行加锁, 里面引入的RedisLock来自于上篇分布式加锁最后一种的实现类.
import com.alibaba.fastjson.JSON; 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.springframework.beans.factory.annotation.Autowired; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import pdfc.framework.exception.BusinessException; import pdfc.platform.common.util.CommonUtil; import pdfc.platform.common.util.RedisLock; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; @Component @Aspect @Slf4j public class GetLockAop { @Autowired private RedisLock redisLock; @Pointcut("@annotation(GetLock)") public void dolock(){ } @Around("dolock() && @annotation(getLock)") public Object doAround(ProceedingJoinPoint pjp, GetLock getLock) throws Throwable { Object proceed = null; String fieldOne = getLock.fieldOne(); String fieldTwo = getLock.fieldTwo(); String methodName = pjp.getSignature().getName();//执行的方法名 Object[] args = pjp.getArgs();//获得方法参数,注意这里是数组 log.info("将要执行:" + methodName + "方法;"); log.info("fieldOne:{}, fieldTwo:{}", fieldOne, fieldTwo); log.info("参数为:{}", JSON.toJSON(args)); try { boolean setLockFlag = false; Map<String, Object> fieldMap = new HashMap<>(); if (CommonUtil.stringIsNotNull(fieldOne) && CommonUtil.stringIsNotNull(fieldTwo) && args != null) { if (args.length == 1) { fieldMap = getfieldsValue(args[0], fieldOne, fieldTwo); } else { fieldMap = getParamValue(args, pjp); } if (fieldMap != null && fieldMap.get(fieldOne) != null && fieldMap.get(fieldTwo) != null) { setLockFlag = true; } } if (setLockFlag) { //增加redis锁, 防止两个线程同时进入 String key = methodName + "_" + fieldMap.get(fieldOne) + "_" + fieldMap.get(fieldTwo); boolean lockFlag = redisLock.lock(key, 20000, 40000); if (lockFlag) { log.info(methodName + "方法加锁成功!"); try { proceed = pjp.proceed();//要执行的方法 } finally { redisLock.unlock(key); log.info(methodName + "方法解锁成功!"); } } else { log.info("加锁失败, 返回友好提示语!"); throw new BusinessException("同一业务正在处理, 请稍后再试!", false); } } else { proceed = pjp.proceed();//要执行的方法 } } catch (Exception e) { log.info("GetLockAop出现异常:{}", e.getMessage()); throw new BusinessException(e.getMessage()); } return proceed; } /**传入参数为对象的时候 * 通过反射 获取当前类的所有属性 * 判断是否存在fieldOne 和fieldTwo * 如果有就存入map中并返回 * @param obj * @param fieldOne * @param fieldTwo */ private Map<String, Object> getfieldsValue(Object obj, String fieldOne, String fieldTwo) { Map<String, Object> fieldMap = new HashMap<>(); String value = ""; Field[] fields = obj.getClass().getDeclaredFields(); for (int j = 0; j < fields.length; j++) { fields[j].setAccessible(true); try { // 字段名 if(fields[j].getName().equals(fieldOne)){ if(fields[j].get(obj) != null){//进行效验 value = String.valueOf(fields[j].get(obj));//赋值 fieldMap.put(fieldOne, value); } }else if(fields[j].getName().equals(fieldTwo)){ if(fields[j].get(obj) != null){ value = String.valueOf(fields[j].get(obj));//赋值 fieldMap.put(fieldTwo, value); } } } catch (Exception e) { e.printStackTrace(); } } return fieldMap; } /** * 传入参数为多个单独参数的时候, 取各个参数值及结果到map中 * @param args * @param pjp * @return */ private Map<String, Object> getParamValue(Object[] args, ProceedingJoinPoint pjp) { String classType = pjp.getTarget().getClass().getName(); String methodName = pjp.getSignature().getName(); Map<String, Object> paramMap = new HashMap<>(); try { // 参数值 Class<?>[] classes = new Class[args.length]; boolean noClassesFlag = false; for (int k = 0; k < args.length; k++) { if (args[k] instanceof MultipartFile || args[k] instanceof ServletRequest || args[k] instanceof ServletResponse) { return null; } if (args[k] == null) { noClassesFlag = true; break; } if (!args[k].getClass().isPrimitive()) { // 当方法参数是封装类型 Class s = args[k].getClass(); classes[k] = s == null ? args[k].getClass() : s; } } ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer(); // 获取指定的方法 Method method = null; if (noClassesFlag) { Method[] methods = Class.forName(classType).getDeclaredMethods(); for (Method ms : methods) { String name = ms.getName(); Class<?>[] paramTypes = ms.getParameterTypes(); if (methodName.equals(name) && classes.length == paramTypes.length) { method = Class.forName(classType).getMethod(methodName, paramTypes); } } if (method == null) { return null; } } else { method = Class.forName(classType).getMethod(methodName, classes); } // 参数名 String[] parameterNames = pnd.getParameterNames(method); // 通过map封装参数和参数值 if (parameterNames != null) { for (int i = 0; i < parameterNames.length; i++) { paramMap.put(parameterNames[i], args[i]); } } } catch (Exception e) { e.printStackTrace(); } return paramMap; } }
也许下一次能用上哦