一.定義註解
@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>
複製程式碼
本人第一次發帖,望大佬給出意見,在下會十分感謝的