美文网首页JavaJava 程序员
SpringBoot 自定义注解+AOP+Redis 实现分布式

SpringBoot 自定义注解+AOP+Redis 实现分布式

作者: 程序花生 | 来源:发表于2021-12-24 16:41 被阅读0次

    一、maven依赖

            <!--    redis    -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
            <!--    aop    -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    

    二、自定义注解类

    1、DistributedLockApi.java(API分布式锁注解)

    import java.lang.annotation.*;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author Andon
     * 2021/12/21
     * <p>
     * 分布式锁注解(API)
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DistributedLockApi {
    
        /**
         * 锁key前缀
         */
        String key() default "lock_api";
    
        /**
         * 锁value
         */
        String value() default "lock_value";
    
        /**
         * 锁超时时间
         */
        long timeout() default 10;
    
        /**
         * 超时时间单位
         */
        TimeUnit timeUnit() default TimeUnit.MINUTES;
    
        /**
         * 被加锁方法执行完是否立即释放锁
         */
        boolean immediatelyUnLock() default true;
    
        /**
         * 等待获取锁时间(秒)
         */
        long waitLockSecondTime() default 0;
    }
    

    2、DistributedLockTask.java(任务分布式锁注解)

    import java.lang.annotation.*;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author Andon
     * 2021/12/21
     * <p>
     * 分布式锁注解(任务)
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DistributedLockTask {
    
        /**
         * 锁key前缀
         */
        String key() default "lock_task";
    
        /**
         * 锁value
         */
        String value() default "lock_value";
    
        /**
         * 超时时间
         */
        long timeout() default 10;
    
        /**
         * 时间单位
         */
        TimeUnit timeUnit() default TimeUnit.MINUTES;
    
        /**
         * 被加锁方法执行完是否立即释放锁
         */
        boolean immediatelyUnLock() default true;
    
        /**
         * 等待获取锁时间(秒)
         */
        long waitLockSecondTime() default 0;
    }
    

    三、切面类

    DistributedLockAspect.java(分布式锁切面)

    import com.alibaba.fastjson.JSONObject;
    import com.andon.springbootdistributedlock.annotation.DistributedLockApi;
    import com.andon.springbootdistributedlock.annotation.DistributedLockTask;
    import com.andon.springbootdistributedlock.exception.DistributedLockException;
    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.aspectj.lang.reflect.MethodSignature;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Component;
    import org.springframework.util.ObjectUtils;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletRequest;
    import java.lang.reflect.Method;
    import java.util.Enumeration;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author Andon
     * 2021/12/21
     * <p>
     * 分布式锁切面
     */
    @SuppressWarnings("DuplicatedCode")
    @Slf4j
    @Aspect
    @Component
    public class DistributedLockAspect {
    
        /**
         * StringRedisTemplate
         */
        @Resource
        private StringRedisTemplate stringRedisTemplate;
    
        /**
         * 任务切点
         */
        @Pointcut("@annotation(com.andon.springbootdistributedlock.annotation.DistributedLockTask)")
        public void taskPointCut() {
        }
    
        /**
         * API切点
         */
        @Pointcut("@annotation(com.andon.springbootdistributedlock.annotation.DistributedLockApi)")
        public void apiPointCut() {
        }
    
        /**
         * 任务加分布式锁
         */
        @Around(value = "taskPointCut() && @annotation(distributedLockTask)")
        public Object taskAround(ProceedingJoinPoint pjp, DistributedLockTask distributedLockTask) {
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            // 获取目标类名方法名
            String className = method.getDeclaringClass().getName();
            String methodName = method.getName();
            // 类名方法名作为分布式锁的key
            String key = distributedLockTask.key() + "_" + className + "_" + methodName;
            // 获取锁
            Boolean status = getLock(key, distributedLockTask.value(), distributedLockTask.timeout(), distributedLockTask.timeUnit(), distributedLockTask.waitLockSecondTime());
            if (!ObjectUtils.isEmpty(status) && status.equals(Boolean.TRUE)) {
                try {
                    Object proceed = pjp.proceed();
                    // 释放锁
                    if (distributedLockTask.immediatelyUnLock()) { //是否立即释放锁
                        unLock(key);
                    }
                    return proceed;
                } catch (Throwable throwable) {
                    log.error("key failure!! key{} error:{}", key, throwable.getMessage());
                }
            }
            log.warn("getLock failure!! key{} 已执行!!", key);
            return null;
        }
    
        /**
         * API加分布式锁
         */
        @Around(value = "apiPointCut() && @annotation(distributedLockApi)")
        public Object apiAround(ProceedingJoinPoint pjp, DistributedLockApi distributedLockApi) throws Exception {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
            String remoteHost = request.getRemoteHost(); //访问者ip
            String method = request.getMethod(); //请求方式
            String uri = request.getRequestURI(); //请求路径
            Enumeration<String> headerNames = request.getHeaderNames();
            JSONObject headers = new JSONObject();
            while (headerNames.hasMoreElements()) {
                String s = headerNames.nextElement();
                headers.put(s, request.getHeader(s)); //请求头
            }
            log.info("headers:{}", headers.toJSONString());
            Object[] args = pjp.getArgs(); //获取入参
            // 类名方法名作为分布式锁的key
            String key = distributedLockApi.key() + "_" + method + "_" + uri + "_" + JSONObject.toJSONString(args);
            // 获取锁
            Boolean status = getLock(key, distributedLockApi.value(), distributedLockApi.timeout(), distributedLockApi.timeUnit(), distributedLockApi.waitLockSecondTime());
            if (!ObjectUtils.isEmpty(status) && status.equals(Boolean.TRUE)) {
                try {
                    Object proceed = pjp.proceed();
                    // 释放锁
                    if (distributedLockApi.immediatelyUnLock()) { //是否立即释放锁
                        unLock(key);
                    }
                    return proceed;
                } catch (Throwable throwable) {
                    log.error("key failure!! key{} error:{}", key, throwable.getMessage());
                }
            }
            log.warn("getLock failure!! key{} 已执行!!", key);
            throw new DistributedLockException("请勿重复操作!!");
        }
    
        /**
         * 获取锁
         */
        private Boolean getLock(String key, String value, long timeout, TimeUnit unit, long waitLockSecondTime) {
            Boolean status = null;
            try {
                long endTime = System.currentTimeMillis() + waitLockSecondTime * 1000;
                do {
                    status = stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
                    Thread.sleep(50);
                } while (System.currentTimeMillis() - endTime < 0 && (ObjectUtils.isEmpty(status) || !status.equals(Boolean.TRUE)));
            } catch (Exception e) {
                log.error("getLock failure!! error:{}", e.getMessage());
            }
            return status;
        }
    
        /**
         * 释放锁
         */
        private void unLock(String key) {
            try {
                stringRedisTemplate.delete(key);
            } catch (Exception e) {
                log.error("unLock failure!! error:{}", e.getMessage());
            }
        }
    }
    

    四、测试类

    1、TaskService.java(测试任务类)

    import com.andon.springbootdistributedlock.annotation.DistributedLockTask;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author Andon
     * 2021/12/21
     */
    @Slf4j
    @Service
    public class TaskService {
    
        @DistributedLockTask(timeout = 1, timeUnit = TimeUnit.DAYS, immediatelyUnLock = false)
        public void task01() {
            log.info("task01 run!!");
        }
    }
    

    2、Main.java(测试执行任务类)

    import com.andon.springbootdistributedlock.task.TaskService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    import java.util.concurrent.ThreadPoolExecutor;
    
    /**
     * @author Andon
     * 2021/12/21
     */
    @Slf4j
    @Component
    public class Main implements ApplicationRunner {
    
        @Resource
        private ThreadPoolExecutor globalExecutorService;
        @Resource
        private TaskService taskService;
    
        @Override
        public void run(ApplicationArguments args) {
            for (int i = 0; i < 6; i++) {
                System.out.println("Run!!");
            }
    
            globalExecutorService.execute(() -> taskService.task01());
        }
    }
    

    3、ApiController.java(测试ApiController类)

    import java.util.concurrent.TimeUnit;
    
    /**
     * @author Andon
     * 2021/12/21
     */
    @Slf4j
    @RequestMapping(value = "/api")
    @RestController
    public class ApiController {
    
        @DistributedLockApi(timeout = 20, timeUnit = TimeUnit.SECONDS, immediatelyUnLock = false, waitLockSecondTime = 5)
        @GetMapping(value = "/test")
        public String test() {
            log.info("test!!");
            return "test!!";
        }
    }
    

    五、测试结果

    1、任务-加分布式锁

    2、Api-加分布式锁

    原文链接:https://blog.csdn.net/weixin_39792935/article/details/122068383
    作者:摸鱼佬

    相关文章

      网友评论

        本文标题:SpringBoot 自定义注解+AOP+Redis 实现分布式

        本文链接:https://www.haomeiwen.com/subject/wzolqrtx.html