zoukankan      html  css  js  c++  java
  • 自定义注解与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;
        }
    }

    也许下一次能用上哦

  • 相关阅读:
    Android JSON解析
    相对靠谱的相册、拍照返回选择程序
    android strings.xml转义字符,注意细节解决
    设置progressBar 背景以及进度条
    Android Launcher简易Launcher开发
    android.os.NetworkOnMainThreadException 异常处理
    AspNetPager 多条件分页查询
    无法解析指定对象的 TargetProperty (UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)“的异常解决
    DefaultButton Deal with users hitting ENTER on your forms
    Setup SSRS in SharePoint Integrated Mode
  • 原文地址:https://www.cnblogs.com/goujh/p/13751447.html
Copyright © 2011-2022 走看看