1. Аннотация к определению
@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.zgiot.zx.common.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author: hlm
* @Date: 2019/4/4 11:14
*/
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//用于注解自建对象
public @interface CacheBody {
}
2. Определите класс аспекта
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;
}
}
3. Файл конфигурации
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");
}
}
4. зависимость от 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>
Пишу впервые, надеюсь на ваше мнение, буду очень признателен