前言
分布式锁应该是日常工作中常见的一种需求,本文将演示如何通过注解的方式,快速实现分布式锁的功能。
一、设计说明
分布式锁的简单实现可以借助Redis来完成,加锁时直接利用SET_IF_ABSENT
来控制,如果SET
成功则表示加锁成功,如果已存在表示业务流程还在锁定中,则直接返回,最后流程处理完之后,将KEY
值删除即可。
二、代码示例
1.自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisDistributedLock {
/**
* 参数名,表示将从哪个参数中获取属性值。
* 获取到的属性值将作为KEY。
* @return
*/
String name() default "";
/**
* 属性,表示将获取哪个属性的值。
* @return
*/
String field() default "";
/**
* 失效时间:单位秒
* @return
*/
long expiration();
/**
* 参数类型
* @return
*/
Class type();
}
2.统一的请求入参对象
可根据自身公司实际情况修改
@Data
public class RequestData<T> {
private Header header;
private T body;
}
@Data
public class Header {
private String token;
}
@Data
public class Order {
String orderId;
}
3.AOP处理
import com.springboot.micrometer.annotation.RedisDistributedLock;
import com.springboot.micrometer.entity.RequestData;
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.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Map;
@Aspect
@Component
public class RedisDistributedLockAspect {
@Resource
private RedisTemplate<String, Serializable> redisTemplate;
@Pointcut("@annotation(com.springboot.micrometer.annotation.RedisDistributedLock)")
public void redisDistributedLock() {
}
@Around("redisDistributedLock()")
public Object methodAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
RedisDistributedLock redisDistributedLock = method.getAnnotation(RedisDistributedLock.class);
String field = redisDistributedLock.field();
String name = redisDistributedLock.name();
long expiration = redisDistributedLock.expiration();
Class clazzType = redisDistributedLock.type();
String fieldValue;
Object object = clazzType.newInstance();
Map<String, Object> paramValue = AopUtils.getParamValue(joinPoint);
if (object instanceof String) {
fieldValue = (String) paramValue.get(name);
} else if (object instanceof RequestData) {
RequestData requestData = (RequestData) paramValue.get(name);
fieldValue = String.valueOf(AopUtils.getFieldValue(requestData.getBody(), field));
} else {
throw new IllegalArgumentException("不支持的参数类型");
}
if (lock(fieldValue, fieldValue, expiration)) {
try {
return joinPoint.proceed();
} finally {
unlock(fieldValue);
}
}
return "重复请求";
}
public void unlock(String key) {
redisTemplate.delete(key);
}
public boolean lock(@NonNull String key, String value, @NonNull long timeout) {
return redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.set(key.getBytes(), value.getBytes(), Expiration.seconds(timeout), RedisStringCommands.SetOption.SET_IF_ABSENT));
}
}
4.请求示例
import com.springboot.micrometer.annotation.RedisDistributedLock;
import com.springboot.micrometer.entity.Order;
import com.springboot.micrometer.entity.RequestData;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/distribute")
public class DistributedLockController {
@RedisDistributedLock(field = "orderId", name = "order", expiration = 10, type = RequestData.class)
@RequestMapping("/query")
public String query(@RequestBody RequestData<Order> order) {
return "success";
}
}