自定义注解与AOP切面实现分布式加锁

  笔者在新公司也需要用到分布式加锁, 由于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;
    }
}

也许下一次能用上哦

原文地址:https://www.cnblogs.com/goujh/p/13751447.html