init
This commit is contained in:
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
136
src/main/java/com/senmesh/frame/redis/dto/LockDTO.java
Normal file
136
src/main/java/com/senmesh/frame/redis/dto/LockDTO.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
148
src/main/java/com/senmesh/frame/redis/util/IAopUtils.java
Normal file
148
src/main/java/com/senmesh/frame/redis/util/IAopUtils.java
Normal 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;
|
||||
}
|
||||
}
|
||||
84
src/main/java/com/senmesh/frame/redis/util/LockUtils.java
Normal file
84
src/main/java/com/senmesh/frame/redis/util/LockUtils.java
Normal 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;
|
||||
}
|
||||
}
|
||||
323
src/main/java/com/senmesh/frame/redis/util/RedisUtils.java
Normal file
323
src/main/java/com/senmesh/frame/redis/util/RedisUtils.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user