基於註解的方式使用spring-integration-redis分散式鎖

家有仙妻發表於2019-04-08

一.定義註解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface CacheLock {

  String prefix() default "";

//  int expire() default 5;

//  TimeUnit timeUnit() default TimeUnit.SECONDS;

  String delimiter() default ":";
}
複製程式碼
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface CacheParam {

  String name() default "";

  int order() default -1;
}
複製程式碼

二.定義切面類

package com.xxx.xx.common.aop;

import com.alibaba.excel.util.StringUtils;
import com.xxx.xx.common.annotation.CacheBody;
import com.xxx.xx.common.annotation.CacheLock;
import com.xxx.xx.common.annotation.CacheParam;
import com.xxx.xx.common.aop.dto.CacheParamDTO;
import com.xxx.xx.common.dto.Ret;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.locks.Lock;
import org.apache.commons.lang3.ArrayUtils;
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.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Component;

/**
 * @Author: hlm
 * @Date: 2019/4/4 10:46
 */
@Aspect
@Component
public class CacheLockAop {

  @Autowired
  private RedisLockRegistry redisLockRegistry;

  @Pointcut("@annotation(cacheLock)")
  public void cacheLockPointCut(CacheLock cacheLock) {
  }

  @Around("cacheLockPointCut(cacheLock)")
  public Object around(ProceedingJoinPoint joinPoint, CacheLock cacheLock)
      throws Throwable {

    MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    Method method = methodSignature.getMethod();
    if (StringUtils.isEmpty(cacheLock.prefix())) {
      throw new RuntimeException("key不能為空, 請在@CacheLock中定義prefix=%s");
    }

    CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();
    String[] parameterNames = codeSignature.getParameterNames();
    //獲取方法入參,組裝key
    List<CacheParamDTO> cacheParamDTOList = new ArrayList<>();
    Annotation[][] parameterAnnotations = method.getParameterAnnotations();
    for (Annotation[] parameterAnnotation : parameterAnnotations) {
      int paramIndex = ArrayUtils.indexOf(parameterAnnotations, parameterAnnotation);
      for (Annotation annotation : parameterAnnotation) {
        if (annotation instanceof CacheParam) {
          CacheParamDTO cacheParamDTO = new CacheParamDTO();
          if (StringUtils.isEmpty(((CacheParam) annotation).name())) {
            cacheParamDTO.setName(parameterNames[paramIndex]);
          } else {
            cacheParamDTO.setName(((CacheParam) annotation).name());
          }
          cacheParamDTO.setOrder(((CacheParam) annotation).order());
          cacheParamDTO.setValue(joinPoint.getArgs()[paramIndex].toString());
          cacheParamDTOList.add(cacheParamDTO);
        }
        if (annotation instanceof CacheBody) {
          Object obj = joinPoint.getArgs()[paramIndex];
          Class bodyClass = obj.getClass();
          Field[] fields = bodyClass.getDeclaredFields();
          for (Field field : fields) {
            if (field.isAnnotationPresent(CacheParam.class)) {
              Annotation fieldAnnotation = field.getAnnotation(CacheParam.class);
              field.setAccessible(true);
              CacheParamDTO cacheParamDTO = new CacheParamDTO();
              if (StringUtils.isEmpty(((CacheParam) fieldAnnotation).name())) {
                cacheParamDTO.setName(field.getName());
              } else {
                cacheParamDTO.setName(((CacheParam) fieldAnnotation).name());
              }
              cacheParamDTO.setOrder(((CacheParam) fieldAnnotation).order());
              cacheParamDTO.setValue((String) field.get(obj));
              cacheParamDTOList.add(cacheParamDTO);
            }
          }
        }
      }
    }
    StringBuilder builder = new StringBuilder();
    cacheParamDTOList.sort(Comparator.comparing(item -> item.getOrder()));
    for (CacheParamDTO cacheParamDTO : cacheParamDTOList) {
      builder.append(cacheLock.delimiter())
          .append(cacheParamDTO.getName())
          .append(cacheLock.delimiter())
          .append(cacheParamDTO.getValue());
    }
    final String key = cacheLock.prefix() + builder.toString();
    
    //spring-integration對redis分佈鎖的支援,底層應該也是lua指令碼的實現,可完美解決執行緒掛掉造成的死鎖,以及執行時間過長鎖釋放掉,誤刪別人的鎖
    Lock lock = redisLockRegistry.obtain(key);
    Boolean lockFlag = lock.tryLock();
    if (!lockFlag) {
      return new Ret<>("M4000", "系統繁忙,請重試");
    }
    Object object = null;
    try {
      object = joinPoint.proceed();
    } finally {
      lock.unlock();
    }
    return object;
  }
}
複製程式碼

三.配置檔案

package com.xxx.xx.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.integration.redis.util.RedisLockRegistry;

/**
 * @Author: hlm
 * @Date: 2019/4/8 8:59
 */
@Configuration
public class RedisLockConfiguration {

  @Bean
  public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
    return new RedisLockRegistry(redisConnectionFactory, "locks");
  }
}

複製程式碼

四.maven依賴

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-integration</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.integration</groupId>
      <artifactId>spring-integration-redis</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
      <version>2.0.4.RELEASE</version>
    </dependency>
複製程式碼

本人第一次發帖,望大佬給出意見,在下會十分感謝的

相關文章