SpringBoot 自定义注解+AOP+Redis 实现分布式锁做幂等

SpringBoot 自定义注解+AOP+Redis 实现分布式锁做幂等

    • 一、maven依赖
    • 二、自定义注解类
      • 1、DistributedLockApi.java(API分布式锁注解)
      • 2、DistributedLockTask.java(任务分布式锁注解)
    • 三、切面类
      • DistributedLockAspect.java(分布式锁切面)
    • 四、测试类
      • 1、TaskService.java(测试任务类)
      • 2、Main.java(测试执行任务类)
      • 3、ApiController.java(测试ApiController类)
    • 五、测试结果
      • 1、任务-加分布式锁
      • 2、Api-加分布式锁

GitHub: link. 欢迎star

注意:本篇博客风格(不多比比就是撸代码!!!)

一、maven依赖

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-aopartifactId>
        dependency>

二、自定义注解类

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

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/**
 * @author Andon
 * 2021/12/21
 * 

* 分布式锁注解(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
 * 

* 分布式锁注解(任务) */ @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
 * 

* 分布式锁切面 */ @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) { 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、任务-加分布式锁

SpringBoot 自定义注解+AOP+Redis 实现分布式锁做幂等_第1张图片
在这里插入图片描述
SpringBoot 自定义注解+AOP+Redis 实现分布式锁做幂等_第2张图片

2、Api-加分布式锁

SpringBoot 自定义注解+AOP+Redis 实现分布式锁做幂等_第3张图片
在这里插入图片描述
在这里插入图片描述
SpringBoot 自定义注解+AOP+Redis 实现分布式锁做幂等_第4张图片
GitHub: link. 欢迎star

你可能感兴趣的:(工具类,分布式锁,AOP,spring,boot,分布式锁,aop)