This commit is contained in:
ran.shang
2025-12-29 16:06:48 +08:00
parent b853edcfde
commit ee95dfa2c2
1600 changed files with 6997 additions and 7445 deletions

View File

@@ -0,0 +1,17 @@
package com.senmesh.frame.redis.aop.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自动解锁
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoUnLock {
}

View File

@@ -0,0 +1,60 @@
package com.senmesh.frame.redis.aop.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
/**
* @author dylanyuan
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Repeatable(RedissonLocks.class)
public @interface RedissonLock {
/**
* 前缀
* @return
*/
String prefix() default "";
/**
* 指定参数名 只支持字符串和字符串数组
* @return
*/
String paramName();
/**
* 超时时间 默认-1:即为1天失效
* 不允许不添加失效时间
* @return
*/
long expires() default -1L;
/**
* 时间单位,默认为秒
* @return
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
/**
* 尝试加锁等待时时间 默认0,
* -1则不进行trylock
* @return
*/
long tryLockWaitTime() default 0;
/**
* 允许param值为空则跳过加锁
* @return
*/
boolean skipIfNull() default false;
}

View File

@@ -0,0 +1,21 @@
package com.senmesh.frame.redis.aop.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 dylanyuan
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Inherited
public @interface RedissonLocks {
RedissonLock[] value();
}

View File

@@ -0,0 +1,66 @@
package com.senmesh.frame.redis.aop.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;
import java.util.concurrent.TimeUnit;
/**
* @author dylanyuan
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface RedissonMultiLock {
/**
* 前缀
* @return
*/
String prefix() default "";
/**
* 指定参数名 只支持字符串和字符串数组
* @return
*/
String paramName();
/**
* 超时时间 默认-1:即为1天失效
* 不允许不添加失效时间
* @return
*/
long expires() default -1L;
/**
* 时间单位 默认为秒
* @return
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
/**
* 尝试加锁等待时时间 默认-1则不进行trylock
* @return
*/
long tryLockWaitTime() default -1;
/**
* 是否允许部分成功
* true, 入参必须存在Set<String> failSet, 失败的会从加入失败队列,原始数据不移除
* false, 不允许部分成功,所有的lock,必须全部成功
* 默认是false
* @return
*/
boolean allowPartSuccess() default false;
/**
* 允许param值为空则跳过加锁
* @return
*/
boolean skipIfNull() default false;
}

View File

@@ -0,0 +1,44 @@
package com.senmesh.frame.redis.aop.aspect;
import com.senmesh.frame.redis.aop.annotation.RedissonLock;
import com.senmesh.frame.redis.util.IAopUtils;
import com.senmesh.frame.redis.util.LockUtils;
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.redisson.api.RLock;
import org.springframework.stereotype.Component;
/**
* @author dylanyuan
*/
@Aspect
@Component
@Slf4j
public class RedissonLockAop {
@Pointcut("@annotation(com.senmesh.frame.redis.aop.annotation.RedissonLock)")
public void annotationPointCut() {
}
@Around("annotationPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("redisson lock {}方法 加锁开始:{}", joinPoint.getSignature().getName(),
joinPoint.toString());
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
RedissonLock redissonLock = signature.getMethod().getAnnotation(RedissonLock.class);
RLock lock = null;
try {
lock = IAopUtils.lock(joinPoint, redissonLock);
return joinPoint.proceed();
} finally {
LockUtils.unlock(lock);
log.info("redisson lock {}方法 加锁结束:{}", joinPoint.getSignature().getName(), joinPoint.toString());
}
}
}

View File

@@ -0,0 +1,52 @@
package com.senmesh.frame.redis.aop.aspect;
import com.senmesh.frame.redis.aop.annotation.RedissonLock;
import com.senmesh.frame.redis.aop.annotation.RedissonLocks;
import com.senmesh.frame.redis.util.IAopUtils;
import com.senmesh.frame.redis.util.LockUtils;
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.redisson.api.RLock;
import org.springframework.stereotype.Component;
/**
* @author dylanyuan
*/
@Aspect
@Component
@Slf4j
public class RedissonLocksAop {
@Pointcut("@annotation(com.senmesh.frame.redis.aop.annotation.RedissonLocks)")
public void annotationPointCut() {
}
@Around("annotationPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("redisson locks {}方法 加锁开始:{}", joinPoint.getSignature().getName(), joinPoint.toString());
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
RedissonLocks redissonLocks = signature.getMethod().getAnnotation(RedissonLocks.class);
RedissonLock[] redissonLockArray = redissonLocks.value();
RLock[] locks = new RLock[redissonLockArray.length];
try {
for (int i = 0; i < redissonLockArray.length; i++) {
locks[i] = IAopUtils.lock(joinPoint, redissonLockArray[i]);
}
return joinPoint.proceed();
}finally {
for (RLock lock : locks) {
try {
LockUtils.unlock(lock);
}catch (Exception e){
log.error("redisson locks 锁【{}】解锁失败", lock.getName(), e);
}
}
log.info("redisson locks {}方法 加锁结束:{}", joinPoint.getSignature().getName(),
joinPoint.toString());
}
}
}

View File

@@ -0,0 +1,166 @@
package com.senmesh.frame.redis.aop.aspect;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
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.redisson.api.RLock;
import org.springframework.stereotype.Component;
import com.senmesh.common.util.JsonUtils;
import com.senmesh.frame.redis.aop.annotation.RedissonMultiLock;
import com.senmesh.frame.redis.exception.RedisLockErrorParamException;
import com.senmesh.frame.redis.exception.RedisLockFailException;
import com.senmesh.frame.redis.redisson.RedissonUtils;
import com.senmesh.frame.redis.util.IAopUtils;
import com.senmesh.frame.redis.util.LockUtils;
import lombok.extern.slf4j.Slf4j;
/**
* @author dylanyuan
*/
@Aspect
@Component
@Slf4j
public class RedissonMultiLockAop {
public static final String FAIL_SET = "failSet";
@Pointcut("@annotation(com.senmesh.frame.redis.aop.annotation.RedissonMultiLock)")
public void annotationPointCut() {}
@Around("annotationPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
log.info("redisson batchlock {}方法 加锁开始:{}", methodName, joinPoint.toString());
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
RedissonMultiLock redisMultiLock = signature.getMethod().getAnnotation(RedissonMultiLock.class);
String paramName = redisMultiLock.paramName();
if (StringUtils.isBlank(paramName)) {
throw new RedisLockErrorParamException(
"redisson batchlock 方法 " + methodName + " paramName不能为空");
}
Object paramValue = IAopUtils.getParamValue(joinPoint, paramName);
if (paramValue == null) {
boolean skipIfNull = redisMultiLock.skipIfNull();
if (skipIfNull) {
return joinPoint.proceed();
}
throw new RedisLockErrorParamException(
"redisson batchlock 方法 " + methodName + " paramName[" + paramName + "]所对应的参数内容不能为空");
}
IAopUtils.checkParamType(IAopUtils.getMethod(joinPoint), paramName, paramValue, true);
boolean allowPartSuccess = redisMultiLock.allowPartSuccess();
Collection<String> failSet = null;
if (allowPartSuccess) {
try {
Object temp = IAopUtils.getParamValue(joinPoint, FAIL_SET);
if (temp == null) {
throw new RedisLockErrorParamException("redisson batchlock 方法 " + methodName
+ " 使用部分成功加锁时必须存在失败集合参数[Collection<String> failSet]");
}
failSet = (Collection<String>) temp;
} catch (Exception e) {
throw new RedisLockErrorParamException(
"redisson batchlock 方法 " + methodName + " 使用部分成功加锁时必须存在失败集合参数[Set<String> failSet]");
}
} else {
failSet = new ArrayList<>();
}
long expires = redisMultiLock.expires();
TimeUnit timeUnit = redisMultiLock.timeUnit();
if (expires == -1) {
expires = 1;
timeUnit = TimeUnit.DAYS;
}
long tryLockWaitTime = redisMultiLock.tryLockWaitTime();
Map<String, String> key2ParamMap = new HashMap<>();
if (paramValue instanceof String[]) {
String[] values = (String[]) paramValue;
for (String value : values) {
key2ParamMap.put(redisMultiLock.prefix() + value, value);
}
} else if (paramValue instanceof Iterable) {
Iterable<String> iterable = (Iterable<String>) paramValue;
Iterator<String> iterator = iterable.iterator();
while (iterator.hasNext()) {
String value = iterator.next();
key2ParamMap.put(redisMultiLock.prefix() + value, value);
}
}
return partSuccessBatchLock(joinPoint, expires, timeUnit, tryLockWaitTime, key2ParamMap,
paramValue, failSet, allowPartSuccess);
}
private Object partSuccessBatchLock(ProceedingJoinPoint joinPoint, long expires,
TimeUnit timeUnit, long tryLockWaitTime, Map<String, String> key2ParamMap, Object paramValue,
Collection<String> failSet, boolean allowPartSuccess) throws Throwable {
Map<String, RLock> map = new HashMap<>(key2ParamMap.size());
for (String key : key2ParamMap.keySet()) {
map.put(key, RedissonUtils.getLock(key));
}
try {
for (Entry<String, RLock> entry : map.entrySet()) {
LockUtils.lock(expires, timeUnit, tryLockWaitTime, entry.getValue());
if (!entry.getValue().isLocked()) {
String value = key2ParamMap.get(entry.getKey());
log.error("方法[{}]所对应的参数[{}]正在运行中,请不要重复操作!", joinPoint.getSignature().getName(), value);
failSet.add(value);
}
}
if (allowPartSuccess) {
if (map.size() == failSet.size()) {
throw new RedisLockFailException(
"方法[" + joinPoint.getSignature().getName() + "]所对应的参数["
+ JsonUtils.toJsonString(key2ParamMap.values())
+ "]正在运行中,请不要重复操作!");
} else if (failSet.size() > 0) {
if (paramValue instanceof String[]) {
String[] values = (String[]) paramValue;
for (int i = 0; i < values.length; i++) {
if (failSet.contains(values[i])) {
values[i] = null;
}
}
} else if (paramValue instanceof Iterable) {
Iterable<String> iterable = (Iterable<String>) paramValue;
Iterator<String> iterator = iterable.iterator();
while (iterator.hasNext()) {
if (failSet.contains(iterator.next())) {
iterator.remove();
}
}
}
}
} else {
if (failSet.size() > 0) {
throw new RedisLockFailException("方法[" + joinPoint.getSignature().getName() + "]所对应的参数["
+ JsonUtils.toJsonString(key2ParamMap.values()) + "]正在运行中,请不要重复操作!");
}
}
return joinPoint.proceed();
} finally {
for (RLock lock : map.values()) {
try {
LockUtils.unlock(lock);
} catch (Exception e) {
log.error("redisson batchlock 锁【{}】解锁失败", lock.getName(), e);
}
}
log.info("redisson batchlock {}方法 加锁结束:{}", joinPoint.getSignature().getName(),
joinPoint.toString());
}
}
}

View File

@@ -0,0 +1,33 @@
package com.senmesh.frame.redis.aop.aspect;
import com.senmesh.frame.redis.redisson.RedissonUtils;
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.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 自动解锁
*/
@Aspect
@Component
@Order(1)
@Slf4j
public class UnLockAutomaticallyAop {
@Pointcut("@annotation(com.senmesh.frame.redis.aop.annotation.AutoUnLock)")
public void annotationPointCut() {
}
@Around("annotationPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
return joinPoint.proceed();
} finally {
RedissonUtils.unLockAutomatically();
}
}
}

View File

@@ -0,0 +1,110 @@
package com.senmesh.frame.redis.config;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizer;
import org.springframework.boot.autoconfigure.cache.CacheManagerCustomizers;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.autoconfigure.cache.CacheProperties.Redis;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheManager.RedisCacheManagerBuilder;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
/**
* 参考 org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
* org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
*
* @author sr on 2020-07-16
*/
@Configuration
@AutoConfigureAfter({RedisAutoConfiguration.class})
@ConditionalOnClass(RedisConnectionFactory.class)
//@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(RedisCacheManager.class)
@EnableConfigurationProperties({CacheProperties.class})
public class RedisCacheConfig {
private final CacheProperties cacheProperties;
/** CacheAutoConfiguration中定义,自动注入 **/
@Autowired
private CacheManagerCustomizers customizerInvoker;
private final org.springframework.data.redis.cache.RedisCacheConfiguration redisCacheConfiguration;
RedisCacheConfig(CacheProperties cacheProperties,
ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration) {
this.cacheProperties = cacheProperties;
this.redisCacheConfiguration = redisCacheConfiguration.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean
public CacheManagerCustomizers cacheManagerCustomizers(
ObjectProvider<CacheManagerCustomizer<?>> customizers) {
return new CacheManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));
}
/**
* redis缓存,默认缓存
*
* @param redisConnectionFactory
* @param resourceLoader
* @return
* @author sr on 2020-07-16
*/
@Bean
@Primary
public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory,
ResourceLoader resourceLoader) {
RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(determineConfiguration(resourceLoader.getClassLoader()));
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
}
return this.customizerInvoker.customize(builder.build());
}
private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(
ClassLoader classLoader) {
if (this.redisCacheConfiguration != null) {
return this.redisCacheConfiguration;
}
Redis redisProperties = this.cacheProperties.getRedis();
org.springframework.data.redis.cache.RedisCacheConfiguration config =
org.springframework.data.redis.cache.RedisCacheConfiguration
.defaultCacheConfig();
config = config.serializeValuesWith(
SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix();
}
return config;
}
}

View File

@@ -0,0 +1,54 @@
package com.senmesh.frame.redis.config;
import org.redisson.api.RedissonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.senmesh.frame.redis.redisson.RedissonUtils;
import lombok.extern.slf4j.Slf4j;
/**
* @author Jerry.li
* @description Redis配置类
* @date 2020/4/16
*/
@Slf4j
@Configuration
public class RedisConfig {
@SuppressWarnings({"rawtypes", "unchecked"})
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
log.info("--------RedisConfig redisTemplate 初始化----->>");
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer(Object.class);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public RedissonUtils redissonUtils(RedissonClient redissonClient) {
return new RedissonUtils(redissonClient);
}
}

View File

@@ -0,0 +1,136 @@
package com.senmesh.frame.redis.dto;
import com.senmesh.common.util.EnvironmentUtils;
import lombok.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author chenyun
* @description 锁的key对象
* @date 2020/9/11 13:44
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class LockDTO implements Serializable {
private static final long serialVersionUID = 5125024940029525121L;
private static final String SPLIT;
private static final Long DEFAULT_TIME;
static {
SPLIT = ":";
DEFAULT_TIME = 60L;
}
/**
* 系统编码
*/
private Integer systemCode;
/**
* 锁执行的业务方法名
*/
private String methodName;
/**
* 锁对象其他参数
*/
@Singular
private List<String> args;
/**
* 锁的key
*/
private String lockKey;
/**
* 时间单位
*/
private TimeUnit unit;
/**
* 尝试获取锁的时间,超过这个时间表示获取锁失败
*/
private Long tryLockWaitTime;
/**
* 锁对象存活时间,超过这个时间,锁自动释放
*/
private Long lockTimeout;
private String failMsg;
public TimeUnit getUnit() {
if (this.unit == null) {
this.unit = TimeUnit.SECONDS;
}
return this.unit;
}
public Long getTryLockWaitTime() {
if (this.tryLockWaitTime == null) {
this.tryLockWaitTime = DEFAULT_TIME;
}
return this.tryLockWaitTime;
}
public Long getLockTimeout() {
if (this.lockTimeout == null) {
this.lockTimeout = DEFAULT_TIME;
}
return this.lockTimeout;
}
public String getLockKey() {
return StringUtils.isEmpty(lockKey) ? lockKey = invoke() : lockKey;
}
public Integer getSystem() {
if (systemCode == null) {
this.systemCode = EnvironmentUtils.getSystemCode();
}
return this.systemCode;
}
/**
* 组装lockKey
*
* @return
*/
private String invoke() {
StringBuilder sb = new StringBuilder();
if (getSystem() != null) {
sb.append(getSystem()).append(SPLIT);
}
if (StringUtils.isNotEmpty(getMethodName())) {
sb.append(getMethodName()).append(SPLIT);
}
List<String> args = getArgs();
if (CollectionUtils.isNotEmpty(args)) {
int size = args.size();
for (int i = 0; i < size; i++) {
String arg = args.get(i);
if (StringUtils.isEmpty(arg)) {
continue;
}
sb.append(arg);
if (i < size - 1) {
sb.append(SPLIT);
}
}
}
return sb.toString();
}
}

View File

@@ -0,0 +1,24 @@
package com.senmesh.frame.redis.exception;
/**
* redis锁错误参数异常
* @author dylanyuan
*/
public class RedisLockErrorParamException extends RedisLockException {
private static final long serialVersionUID = -2632401028683380018L;
public RedisLockErrorParamException() {}
public RedisLockErrorParamException(String message) {
super(message);
}
public RedisLockErrorParamException(String message, Throwable cause) {
super(message, cause);
}
public RedisLockErrorParamException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,27 @@
package com.senmesh.frame.redis.exception;
import com.senmesh.common.exception.AlitaException;
/**
* redis锁专用异常父类
* @author dylanyuan
*/
public class RedisLockException extends AlitaException {
private static final long serialVersionUID = -6487400012014351701L;
public RedisLockException() {}
public RedisLockException(String message) {
super(message);
}
public RedisLockException(String message, Throwable cause) {
super(message, cause);
}
public RedisLockException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,24 @@
package com.senmesh.frame.redis.exception;
/**
* 加锁失败异常
* @author dylanyuan
*/
public class RedisLockFailException extends RedisLockException {
private static final long serialVersionUID = 8204346409836254547L;
public RedisLockFailException() {}
public RedisLockFailException(String message) {
super(message);
}
public RedisLockFailException(String message, Throwable cause) {
super(message, cause);
}
public RedisLockFailException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,18 @@
package com.senmesh.frame.redis.property;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @Author dylanyuan
* @Date 2020/12/10 11:37 上午
* @Version 1.0
*/
@ConfigurationProperties(
prefix = "alita.redisson"
)
@Data
public class RedissonProperties {
private int pingConnectionInterval = 0;
}

View File

@@ -0,0 +1,158 @@
//package com.newyetai.frame.redis.redisson;
//
//import com.newyetai.frame.redis.property.RedissonProperties;
//import java.lang.reflect.Method;
//import java.util.ArrayList;
//import java.util.Arrays;
//import java.util.List;
//
//import org.apache.commons.lang3.StringUtils;
//import org.redisson.Redisson;
//import org.redisson.api.RedissonClient;
//import org.redisson.config.Config;
//import org.redisson.spring.data.connection.RedissonConnectionFactory;
////import org.redisson.spring.data.connection.RedissonConnectionFactory;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.boot.autoconfigure.AutoConfigureAfter;
//import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
//import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
//import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
//import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
//import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel;
//import org.springframework.boot.context.properties.EnableConfigurationProperties;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.context.annotation.Import;
//import org.springframework.data.redis.connection.RedisConnectionFactory;
//import org.springframework.data.redis.core.RedisOperations;
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.data.redis.core.StringRedisTemplate;
//import org.springframework.util.ReflectionUtils;
//
///**
// * 参考redisson-spring-boot-starter的RedissonAutoConfiguration,沿用redis配置,移除自定义配置文件解析配置
// *
// * @author sr on 2020-05-26
// */
//@Configuration
//@ConditionalOnClass({Redisson.class, RedisOperations.class})
//@AutoConfigureAfter({RedisAutoConfiguration.class})
//@EnableConfigurationProperties({RedisProperties.class, RedissonProperties.class})
//@Import({RedisAutoConfiguration.class})
//public class RedissonConfig {
//
// @Autowired
// private RedisProperties redisProperties;
//
// @Autowired
// private RedissonProperties redissonProperties;
//
// @Bean
// @ConditionalOnMissingBean(name = "redisTemplate")
// public RedisTemplate<Object, Object> redisTemplate(
// RedisConnectionFactory redisConnectionFactory) {
// RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
// template.setConnectionFactory(redisConnectionFactory);
// return template;
// }
//
// @Bean
// @ConditionalOnMissingBean(StringRedisTemplate.class)
// public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
// StringRedisTemplate template = new StringRedisTemplate();
// template.setConnectionFactory(redisConnectionFactory);
// return template;
// }
//
// @Bean
// @ConditionalOnMissingBean(RedisConnectionFactory.class)
// public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) {
// return new RedissonConnectionFactory(redisson);
// }
//
// @SuppressWarnings("unchecked")
// @Bean(destroyMethod = "shutdown")
// @ConditionalOnMissingBean(RedissonClient.class)
// public RedissonClient redisson() {
// Config config = null;
// Method clusterMethod = ReflectionUtils.findMethod(RedisProperties.class, "getCluster");
// Method timeoutMethod = ReflectionUtils.findMethod(RedisProperties.class, "getTimeout");
// Object timeoutValue = ReflectionUtils.invokeMethod(timeoutMethod, redisProperties);
// int timeout;
// if (null == timeoutValue) {
// timeout = 0;
// } else if (!(timeoutValue instanceof Integer)) {
// Method millisMethod = ReflectionUtils.findMethod(timeoutValue.getClass(), "toMillis");
// timeout = ((Long) ReflectionUtils.invokeMethod(millisMethod, timeoutValue)).intValue();
// } else {
// timeout = (Integer) timeoutValue;
// }
//
// // 修复不设置密码,传入空字符串,置为空
// String password = redisProperties.getPassword();
// password = StringUtils.isBlank(password) ? null : password;
// if (redisProperties.getSentinel() != null) {
// Method nodesMethod = ReflectionUtils.findMethod(Sentinel.class, "getNodes");
// Object nodesValue = ReflectionUtils.invokeMethod(nodesMethod, redisProperties.getSentinel());
//
// String[] nodes;
// if (nodesValue instanceof String) {
// nodes = convert(Arrays.asList(((String) nodesValue).split(",")));
// } else {
// nodes = convert((List<String>) nodesValue);
// }
//
// config = new Config();
// config.useSentinelServers()
// .setPingConnectionInterval(redissonProperties.getPingConnectionInterval())
// .setMasterName(redisProperties.getSentinel().getMaster())
// .addSentinelAddress(nodes).setDatabase(redisProperties.getDatabase())
// .setConnectTimeout(timeout).setPassword(password);
// } else if (clusterMethod != null
// && ReflectionUtils.invokeMethod(clusterMethod, redisProperties) != null) {
// Object clusterObject = ReflectionUtils.invokeMethod(clusterMethod, redisProperties);
// Method nodesMethod = ReflectionUtils.findMethod(clusterObject.getClass(), "getNodes");
// List<String> nodesObject = (List<String>) ReflectionUtils.invokeMethod(nodesMethod, clusterObject);
//
// String[] nodes = convert(nodesObject);
//
// config = new Config();
// config.useClusterServers()
// .setPingConnectionInterval(redissonProperties.getPingConnectionInterval())
// .addNodeAddress(nodes).setConnectTimeout(timeout)
// .setPassword(password);
// } else {
// config = new Config();
// String prefix = "redis://";
// Method method = ReflectionUtils.findMethod(RedisProperties.class, "isSsl");
// if (method != null && (Boolean) ReflectionUtils.invokeMethod(method, redisProperties)) {
// prefix = "rediss://";
// }
//
// config.useSingleServer()
// .setPingConnectionInterval(redissonProperties.getPingConnectionInterval())
// .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
// .setConnectTimeout(timeout).setDatabase(redisProperties.getDatabase())
// .setPassword(password);
// }
// return Redisson.create(config);
// }
//
// private String[] convert(List<String> nodesObject) {
// List<String> nodes = new ArrayList<String>(nodesObject.size());
// for (String node : nodesObject) {
// if (!node.startsWith("redis://") && !node.startsWith("rediss://")) {
// nodes.add("redis://" + node);
// } else {
// nodes.add(node);
// }
// }
// return nodes.toArray(new String[nodes.size()]);
// }
//
// @Bean
// public RedissonUtils redissonUtils() {
// return new RedissonUtils(redisson());
// }
//
//}

View File

@@ -0,0 +1,380 @@
package com.senmesh.frame.redis.redisson;
import com.google.common.collect.Lists;
import com.senmesh.common.dto.IBaseDTO;
import com.senmesh.common.exception.ExistException;
import com.senmesh.common.exception.util.Assert;
import com.senmesh.common.function.TransactionAfterCommit;
import com.senmesh.frame.redis.dto.LockDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.RedissonMultiLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* 使用@Autowired注入工具类
*
* @author sr on 2020-05-26
*/
@Slf4j
public class RedissonUtils {
private static RedissonClient client;
/**
* 锁对象缓存实现 ,同一个线程可能持有多把锁
*/
private static final ThreadLocal<Stack<LockDTO>> LOCK_DTO_THREAD_LOCAL = new ThreadLocal<>();
public RedissonUtils(RedissonClient redissonClient) {
RedissonUtils.client = redissonClient;
}
public static RedissonClient getClient() {
return client;
}
/**
* 获取锁对象
* @param lockKey
* @return
*/
public static RLock getLock(String lockKey) {
return client.getLock(lockKey);
}
/**
* 获取连所对象
* @param lockKeys
* @return
*/
public static RLock getMultiLock(String... lockKeys) {
RLock[] locks = new RLock[lockKeys.length];
for (int i = 0; i < lockKeys.length; i++) {
locks[i] = client.getLock(lockKeys[i]);
}
return client.getMultiLock(locks);
}
/**
* 获取连所对象
* @param lockKeys
* @return
*/
public static RLock getMultiLock(List<String> lockKeys) {
RLock[] locks = new RLock[lockKeys.size()];
for (int i = 0; i < lockKeys.size(); i++) {
locks[i] = client.getLock(lockKeys.get(i));
}
return client.getMultiLock(locks);
}
/**
* 获取锁(自旋等待锁)
*
* @param lockDTO
* @return
* @author sr on 2020-05-26
*/
public static RLock lock(LockDTO lockDTO) {
RLock lock = client.getLock(lockDTO.getLockKey());
lock.lock();
return lock;
}
/**
* 获取锁(自旋等待锁)
*
* @param lockKey
* @return
* @author sr on 2020-05-26
*/
public static RLock lock(String lockKey) {
RLock lock = client.getLock(lockKey);
lock.lock();
return lock;
}
/**
* 获取超时锁(自旋等待锁)
*
* @param lockKey
* @param timeout 加锁后自动释放时间(秒)
* @return
* @author sr on 2020-05-26
*/
public static RLock lock(String lockKey, long timeout) {
return lock(lockKey, TimeUnit.SECONDS, timeout);
}
/**
* 超时锁
*
* @param lockKey
* @param unit 时间单位
* @param timeout 加锁后自动释放时间
* @return
* @author sr on 2020-05-26
*/
public static RLock lock(String lockKey, TimeUnit unit, long timeout) {
log.info("lockKey:{}" + lockKey);
RLock lock = client.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
/**
* 尝试获取锁(时间-秒)
*
* @param lockDTO
* @return
*/
public static boolean tryLock(LockDTO lockDTO) {
return tryLock(lockDTO.getLockKey(), lockDTO.getUnit(), lockDTO.getTryLockWaitTime(), lockDTO.getLockTimeout());
}
/**
* 将锁对象缓存入ThreadLocal中
* 尝试获取锁(时间-秒)
*
* @param lockDTO 锁对象
* @return true: 加锁成功 false: 加锁失败
*/
public static boolean tryLockAutomatically(LockDTO lockDTO) {
Stack<LockDTO> stack = LOCK_DTO_THREAD_LOCAL.get();
if (Objects.isNull(stack)) {
LOCK_DTO_THREAD_LOCAL.set(stack = new Stack<>());
}
stack.push(lockDTO);
return tryLock(lockDTO);
}
/**
* 自动解锁
*
* @return true: 解锁成功 false: 解锁失败
*/
public static boolean unLockAutomatically() {
Stack<LockDTO> stack = LOCK_DTO_THREAD_LOCAL.get();
if (Objects.nonNull(stack) && !stack.isEmpty()) {
return unlock(stack.pop());
}
return false;
}
/**
* 尝试获取锁(时间-秒)
*
* @param lockKey
* @param waitTime 最多等待时间,未拿到锁返回false
* @param timeout 上锁后自动释放锁时间
* @return
* @author sr on 2020-08-28
*/
public static boolean tryLock(String lockKey, long waitTime, long timeout) {
return tryLock(lockKey, TimeUnit.SECONDS, waitTime, timeout);
}
/**
* 尝试获取锁
*
* @param lockKey
* @param unit 时间单位
* @param waitTime 最多等待时间,未拿到锁返回false
* @param timeout 上锁后自动释放锁时间
* @return
* @author sr on 2020-05-26
*/
public static boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long timeout) {
RLock lock = client.getLock(lockKey);
try {
return lock.tryLock(waitTime, timeout, unit);
} catch (InterruptedException e) {
return false;
}
}
/**
* 释放锁
*
* @param lockKey
* @author sr on 2020-05-26
*/
public static boolean unlock(String lockKey) {
try {
RLock lock = client.getLock(lockKey);
return unlock(lock);
} catch (Exception e) {
log.error("unlock key:{}, error:{}", lockKey, e.getMessage());
return false;
}
}
/**
* 释放锁
*
* @param lockDTO
* @return
*/
public static boolean unlock(LockDTO lockDTO) {
RLock lock = client.getLock(lockDTO.getLockKey());
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
return unlock(lockDTO.getLockKey());
}
return true;
}
/**
* 释放锁
*
* @param lock
* @author sr on 2020-05-26
*/
public static boolean unlock(RLock lock) {
try {
if (!lock.isLocked()) {
return true;
}
lock.unlock();
} catch (Exception e) {
log.error("unlock key:{}, error msg:{}", lock.getName(), e.getMessage());
return false;
}
return true;
}
/**
* 联锁
*
* @param lockKeys
* @return
* @author sr on 2020-05-28
*/
public static RLock multiLock(Set<String> lockKeys) {
RLock[] locks = (RLock[]) lockKeys.stream().map(lockKey -> client.getLock(lockKey)).toArray();
RedissonMultiLock multiLock = new RedissonMultiLock(locks);
multiLock.lock();
return multiLock;
}
/**
* 尝试获取联锁
*
* @param lockKeys
* @param unit 时间单位
* @param waitTime 最多等待时间,未拿到锁返回false
* @param timeout 上锁后自动释放锁时间
* @return
* @author sr on 2020-05-28
*/
public static boolean tryMultiLock(Set<String> lockKeys, TimeUnit unit, long waitTime, long timeout) {
RLock[] locks = new RLock[lockKeys.size()];
locks = lockKeys.stream().map(lockKey -> client.getLock(lockKey)).collect(Collectors.toList()).toArray(locks);
RedissonMultiLock multiLock = new RedissonMultiLock(locks);
try {
return multiLock.tryLock(waitTime, timeout, unit);
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
/**
* 尝试获取联锁
*
* @param lockKeys 锁的钥匙
* @param unit 单位
* @param waitTime 等待时间
* @param timeout 超时
* @return {@link RLock}
*/
public static RLock tryMultiLockAndReturn(Set<String> lockKeys, TimeUnit unit, long waitTime, long timeout) {
RLock multiLock = getMultiLock(Lists.newArrayList(lockKeys));
try {
if (multiLock.tryLock(waitTime, timeout, unit)){
return multiLock;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
/**
* 校验上锁保存(有事务幂等)
* @param <T>
* @param lockDTO
* @param existSupplier
* @param saveSupplier
* @return
* @author sr on 2024-04-18
*/
public static <T extends IBaseDTO> Long lockAndSave(LockDTO lockDTO, Supplier<T> existSupplier,
Supplier<Long> saveSupplier) {
return lockAndSave(lockDTO, existSupplier, saveSupplier, true, null);
}
public static <T extends IBaseDTO> Long lockAndSave(LockDTO lockDTO, Supplier<T> existSupplier,
Supplier<Long> saveSupplier, String existMsg) {
return lockAndSave(lockDTO, existSupplier, saveSupplier, true, existMsg);
}
/**
* 校验上锁保存
* @param <T>
* @param lockDTO 锁
* @param existSupplier 查询存在的dto
* @param saveSupplier 保存
* @param hasTransaction 当前是否有事务
* @param existMsg 存在抛的异常消息,空则不抛异常返回id
* @return
* @author sr on 2024-04-18
*/
public static <T extends IBaseDTO> Long lockAndSave(LockDTO lockDTO, Supplier<T> existSupplier,
Supplier<Long> saveSupplier, boolean hasTransaction, String existMsg) throws ExistException{
IBaseDTO baseDTO = existSupplier.get();
if (baseDTO != null) {
if (StringUtils.isBlank(existMsg)) {
return baseDTO.getId();
} else {
throw new ExistException(existMsg);
}
}
try {
Assert.isTrue(RedissonUtils.tryLock(lockDTO), lockDTO.getFailMsg());
baseDTO = existSupplier.get();
if (baseDTO != null) {
if (StringUtils.isBlank(existMsg)) {
return baseDTO.getId();
} else {
throw new ExistException(existMsg);
}
}
return saveSupplier.get();
} finally {
if (hasTransaction) {
TransactionAfterCommit.doSomeThingAfterCompletion(()->{
RedissonUtils.unlock(lockDTO);
});
} else {
RedissonUtils.unlock(lockDTO);
}
}
}
}

View File

@@ -0,0 +1,148 @@
package com.senmesh.frame.redis.util;
import com.senmesh.frame.redis.aop.annotation.RedissonLock;
import com.senmesh.frame.redis.exception.RedisLockErrorParamException;
import com.senmesh.frame.redis.exception.RedisLockFailException;
import com.senmesh.frame.redis.redisson.RedissonUtils;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
/**
* @author Created by dylan_yuan
*/
@Slf4j
public class IAopUtils {
public static Method getMethod(JoinPoint joinPoint) throws Exception {
String methodName = joinPoint.getSignature().getName();
// 获取目标对象
Class<?> classTarget = joinPoint.getTarget().getClass();
// 获取目标对象类型
Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
// 获取目标对象方法
return classTarget.getMethod(methodName, parameterTypes);
}
public static Type getType(Method method, String paramName) {
Type[] types = method.getGenericParameterTypes();
if(types == null || types.length == 0){
return null;
}
for (Type type : types) {
if (paramName.equals(type.getTypeName())) {
return type;
}
}
return null;
}
public static Object getParamValue(ProceedingJoinPoint joinPoint, String paramName) {
if (joinPoint == null || StringUtils.isBlank(paramName)) {
return null;
}
Object[] args = joinPoint.getArgs();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
String[] names = methodSignature.getParameterNames();
if (names == null || names.length == 0) {
return null;
}
for (int i = 0; i < names.length; i++) {
if (names[i].equals(paramName)) {
return args[i];
}
}
return null;
}
public static Object getParamValue(ProceedingJoinPoint joinPoint, int index) {
if (joinPoint == null || index < 0) {
return null;
}
Object[] args = joinPoint.getArgs();
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
String[] names = methodSignature.getParameterNames();
if (names == null || names.length == 0 || names.length < index) {
return null;
}
return args[index];
}
public static void checkParamType(Method method, String paramName, Object paramValue, boolean isArray){
String prefix = "redis" + (isArray?"batch":"") + "lock";
String suffix = "字符串" + (isArray?"数组或集合":"");
boolean classTypeMatching;
if(isArray){
Type type = getType(method, paramName);
classTypeMatching = paramValue instanceof String[] || (paramValue instanceof Iterable && checkGenericTypeIsString(type));
}else{
classTypeMatching = paramValue instanceof String;
}
if(!classTypeMatching) {
throw new RedisLockErrorParamException(prefix +
" paramName[" + paramName + "]所对应的参数类型必须为" + suffix);
}
}
private static boolean checkGenericTypeIsString(Type type){
if(type == null){
return false;
}
try {
ParameterizedType pt = (ParameterizedType)type;
Type[] tempTypes = pt.getActualTypeArguments();
if(tempTypes == null || tempTypes.length != 1){
return false;
}
Type temp = tempTypes[0];
if("java.lang.String".equals(temp.getTypeName())) {
return true;
}
}catch (Exception e){
log.error("获取方法入参数类型失败,{}", type.getTypeName());
}
return false;
}
public static RLock lock(ProceedingJoinPoint joinPoint, RedissonLock redissonLock)
throws Exception {
String paramName = redissonLock.paramName();
if (StringUtils.isBlank(paramName)) {
throw new RedisLockErrorParamException("redisson lock paramName不能为空");
}
Object paramValue = IAopUtils.getParamValue(joinPoint, paramName);
if (paramValue == null) {
boolean skipIfNull = redissonLock.skipIfNull();
if(skipIfNull){
return null;
}
throw new RedisLockErrorParamException("redisson lock paramName[" + paramName + "]所对应的参数内容不能为空");
}
IAopUtils.checkParamType(IAopUtils.getMethod(joinPoint), paramName, paramValue, false);
long expires = redissonLock.expires();
TimeUnit timeUnit = redissonLock.timeUnit();
if(expires == -1){
expires = 1;
timeUnit = TimeUnit.DAYS;
}
long tryLockWaitTime = redissonLock.tryLockWaitTime();
String key = redissonLock.prefix() + paramValue;
RLock lock = RedissonUtils.getLock(key);
LockUtils.lock(expires, timeUnit, tryLockWaitTime, lock);
if(!lock.isLocked()) {
throw new RedisLockFailException("方法["+joinPoint.getSignature().getName()+"]所对应的参数["+key+"]正在运行中,请不要重复操作!");
}
return lock;
}
}

View File

@@ -0,0 +1,84 @@
package com.senmesh.frame.redis.util;
import com.senmesh.common.exception.util.Assert;
import com.senmesh.frame.redis.redisson.RedissonUtils;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.RedissonMultiLock;
import org.redisson.api.RLock;
/**
* @author dylanyuan
*/
@Slf4j
public class LockUtils {
public static void unlock(RLock lock) {
if (lock == null){
return;
}
if(lock instanceof RedissonMultiLock){
lock.unlock();
return;
}
if(lock.isLocked() && lock.isHeldByCurrentThread()){
lock.unlock();
return;
}
}
public static void lock(long expires, TimeUnit timeUnit, RLock lock) {
lock(expires, timeUnit, -1, lock);
}
public static void lock(long expires, TimeUnit timeUnit, long tryLockWaitTime, RLock lock) {
Assert.isTrue(lock != null, "锁不能为空");
if(tryLockWaitTime == -1){
lock.lock(expires, timeUnit);
}else {
try {
lock.tryLock(tryLockWaitTime, expires, timeUnit);
} catch (InterruptedException e) {
//如果出现异常的话,当前线程立刻解锁
unlock(lock);
log.error("[{}] try lock 失败", lock.getName(), e);
}
}
}
public static boolean trylock(long expires, TimeUnit timeUnit, long tryLockWaitTime, RLock lock) {
Assert.isTrue(lock != null, "锁不能为空");
Assert.isTrue(tryLockWaitTime >=0, "等待时间必须大于-1");
try {
return lock.tryLock(tryLockWaitTime, expires, timeUnit);
} catch (InterruptedException e) {
//如果出现异常的话,当前线程立刻解锁
unlock(lock);
log.error("[{}] try lock 失败", lock.getName(), e);
}
return false;
}
public static RLock simpleLock(long expires, TimeUnit timeUnit, String key) {
return simpleLock(expires, timeUnit, -1, key);
}
public static RLock simpleLock(long expires, TimeUnit timeUnit, long tryLockWaitTime, String key) {
Assert.isTrue(StringUtils.isNotBlank(key), "锁值不能为空");
RLock lock = RedissonUtils.getLock(key);
if(tryLockWaitTime == -1){
lock.lock(expires, timeUnit);
}else {
try {
lock.tryLock(tryLockWaitTime, expires, timeUnit);
} catch (InterruptedException e) {
//如果出现异常的话,当前线程立刻解锁
unlock(lock);
log.error("[{}] try lock 失败", lock.getName(), e);
}
}
return lock;
}
}

View File

@@ -0,0 +1,323 @@
package com.senmesh.frame.redis.util;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import com.google.common.base.Objects;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
/**
*
* @author Jerry.li
* @description Redis工具类
* @date 2020/4/16
*
* 使用案例
* @Autowired private RedisUtils redisUtils; redisUtils.getExpire(key);
*/
@Slf4j
@Component
public class RedisUtils{
@Autowired
private RedisTemplate<Object, Object> redisTemplateClient;
private static RedisTemplate<Object, Object> redisTemplate;
@PostConstruct
private void init() {
redisTemplate = redisTemplateClient;
}
/**
* 指定缓存失效时间
*
* @param key 键
* @param expireTime 过期时间(秒)
* @return
*/
public static void expire(@NonNull String key, long expireTime) {
if (expireTime > 0 && hasKey(key)) {
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public static Long getExpire(@NonNull String key) {
if (!hasKey(key)) {
return null;
}
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public static boolean hasKey(@NonNull String key) {
Boolean hasKey = redisTemplate.hasKey(key);
if (Objects.equal(hasKey, null)) {
return false;
}
return hasKey;
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
public static void remove(@NonNull String key) {
try {
if (hasKey(key)) {
redisTemplate.delete(key);
}
} catch (Exception e) {
log.info("redis remove key:{}, error:{}", key, e.getMessage());
}
}
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public static Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 永久普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public static void add(@NonNull String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public static void add(@NonNull String key, Object value, long time) {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
add(key, value);
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
public static void removeList(@NonNull String key, Object value) {
try {
if (hasKey(key)) {
redisTemplate.opsForList().remove(key,0,value);
}
} catch (Exception e) {
log.info("redis remove key:{} value:{}, error:{}", key, value, e.getMessage());
}
}
/**
* 将list放入缓存(向后追加方式)
* @param key
* @param value
*/
public static void listAddAll(@NonNull String key, Object... value) {
redisTemplate.opsForList().rightPushAll(key,value);
}
/**
* 将list放入缓存(向后追加方式)
*
* @param key 键
* @param value 值
*/
public static void listAdd(@NonNull String key, Object value) {
redisTemplate.opsForList().rightPush(key, value);
}
/**
* 将list放入缓存(向后追加方式)
*
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public static void listAdd(@NonNull String key, Object value, long time) {
listAdd(key, value);
if (time > 0) {
expire(key, time);
}
}
/**
* 获取list缓存的内容
*
* @param key 键
* @return List
*/
public static List<Object> listGet(@NonNull String key) {
return redisTemplate.opsForList().range(key, 0, -1);
}
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return List
*/
public static List<Object> listGet(@NonNull String key, long start, long end) {
return redisTemplate.opsForList().range(key, start, end);
}
/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public static Long getListSize(@NonNull String key) {
return redisTemplate.opsForList().size(key);
}
/**
* HashMap
*
* @param key 键
* @param map 对应多个键值
*/
public static void mapAdd(@NonNull String key, Map<String, Object> map) {
redisTemplate.opsForHash().putAll(key, map);
}
/**
* HashMap 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
*/
public static void mapAdd(@NonNull String key, Map<String, Object> map, long time) {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key redis键
* @param hashKey 项
* @param value 值
*/
public static void mapAdd(@NonNull String key, @NonNull String hashKey, Object value) {
redisTemplate.opsForHash().put(key, hashKey, value);
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param hashKey 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
*/
public static void mapAdd(String key, String hashKey, Object value, long time) {
redisTemplate.opsForHash().put(key, hashKey, value);
if (time > 0) {
expire(key, time);
}
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param hashKey map key 不能为null
*/
public static boolean mapHasValue(@NonNull String key, @NonNull String hashKey) {
return redisTemplate.opsForHash().hasKey(key, hashKey);
}
/**
* HashGet
*
* @param key 键 不能为null
* @param hashKey 项 不能为null
* @return 值
*/
public static Object mapGet(@NonNull String key, @NonNull String hashKey) {
return redisTemplate.opsForHash().get(key, hashKey);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public static Map<Object, Object> mapGet(@NonNull String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* 自增并设置过期时间
* @param key
* @param expireTime
* @return
* @author sr on 2020-08-03
*/
public static long increment(@NonNull String key, long expireTime) {
long data = redisTemplate.opsForValue().increment(key);
if (data == 1L) {
redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
}
return data;
}
/**
* 自减
* @param key
* @return
* @author sr on 2021-06-16
*/
public static long decrement(@NonNull String key) {
return redisTemplate.opsForValue().decrement(key);
}
}

View File

@@ -0,0 +1,44 @@
package com.senmesh.frame.redis.util;
import com.senmesh.frame.redis.redisson.RedissonUtils;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.ObjectUtils;
import org.redisson.api.RMap;
/**
* @Author dylanyuan
* @Date 2020/11/27 下午6:29
* @Version 1.0
*/
public class RedissonHashUtils {
public static void clear(String hashKey){
RedissonUtils.getClient().getMap(hashKey).clear();
}
public static void set(String hashKey, String key, Object value){
RMap<String, Object> rMap = RedissonUtils.getClient().getMap(hashKey);
rMap.put(key, value);
}
public static void set(String hashKey, String key, Object value, Long expires, TimeUnit timeUnit){
RMap<String, Object> rMap = RedissonUtils.getClient().getMap(hashKey);
rMap.expire(expires, timeUnit);
rMap.put(key, value);
}
public static Map<String, Object> get(String hashKey){
return RedissonUtils.getClient().getMap(hashKey);
}
public static Object get(String hashKey, String key){
RMap<String, Object> rMap = RedissonUtils.getClient().getMap(hashKey);
return rMap.get(key);
}
public static String getString(String hashKey, String key){
RMap<String, Object> rMap = RedissonUtils.getClient().getMap(hashKey);
return ObjectUtils.identityToString(rMap.get(key));
}
}

View File

@@ -0,0 +1,33 @@
package com.senmesh.frame.redis.util;
import com.senmesh.frame.redis.redisson.RedissonUtils;
import java.util.concurrent.TimeUnit;
import org.redisson.api.RSet;
/**
* @Author dylanyuan
* @Date 2020/11/27 下午6:29
* @Version 1.0
*/
public class RedissonSetUtils {
public static void set(String setKey, Object value){
RSet<Object> rSet = RedissonUtils.getClient().getSet(setKey);
rSet.add(value);
}
public static void set(String setKey, Object value, Long expires, TimeUnit timeUnit){
RSet<Object> rSet = RedissonUtils.getClient().getSet(setKey);
rSet.expire(expires, timeUnit);
rSet.add(value);
}
public static void clear(String setKey){
RedissonUtils.getClient().getSet(setKey).clear();
}
public static RSet<Object> get(String setKey){
RSet<Object> rSet = RedissonUtils.getClient().getSet(setKey);
return rSet;
}
}

View File

@@ -0,0 +1,39 @@
package com.senmesh.frame.redis.util;
import com.senmesh.frame.redis.redisson.RedissonUtils;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.ObjectUtils;
import org.redisson.api.RBucket;
/**nng
* @Author dylanyuan
* @Date 2020/11/27 下午6:29
* @Version 1.0
*/
public class RedissonValueUtils {
public static void set(String key, Object value){
RBucket<Object> rBucket = RedissonUtils.getClient().getBucket(key);
rBucket.set(value);
}
public static void set(String key, Object value, Long expires, TimeUnit timeUnit){
RBucket<Object> rBucket = RedissonUtils.getClient().getBucket(key);
rBucket.set(value, expires, timeUnit);
}
public static boolean hasKey(String key){
RBucket<Object> rBucket = RedissonUtils.getClient().getBucket(key);
return rBucket.isExists();
}
public static Object get(String key){
RBucket<Object> rBucket = RedissonUtils.getClient().getBucket(key);
return rBucket.get();
}
public static String getString(String key){
RBucket<Object> rBucket = RedissonUtils.getClient().getBucket(key);
return ObjectUtils.identityToString(rBucket.get());
}
}