All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ideaaedi.commonspring.lock.DefaultRedisLockSupport Maven / Gradle / Ivy

The newest version!
package com.ideaaedi.commonspring.lock;

import com.ideaaedi.commonds.exception.ValidateException;
import com.ideaaedi.commonds.function.NoArgConsumer;
import com.ideaaedi.commonds.function.NoArgFunction;
import com.ideaaedi.commonds.lock.RedisLockSupport;
import com.ideaaedi.commonds.validate.Validator;
import com.ideaaedi.commonspring.parser.SpelUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.lang.NonNull;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * redis分布式锁默认实现
 * 

* 使用示例见{@link com.ideaaedi.commonspring.RedisLockSupportTest} *

* * @author JustryDeng * @since 2022/4/19 10:08 */ @Getter public class DefaultRedisLockSupport implements RedisLockSupport { /** 获取锁时,不等待,获取不到直接失败 */ public static final long NO_WAIT = 0L; /** 默认的redisson客户端 */ private static volatile RedissonClient defaultRedissonClient; /** redisson客户端(优先级高于defaultRedissonClient,当redissonClient不为null时,使用redissonClient) */ protected RedissonClient redissonClient; /** 锁 key */ protected final String lockKey; /** * 锁等待时长 (取值范围:waitTime >= 0) *
0:表示不等待 */ protected long waitTime = NO_WAIT; /** * 持有锁的最大时长 (取值范围:leaseTime > 0 || leaseTime == -1) *
-1:表示自动续期 */ protected long leaseTime = -1L; /** waitTime和leaseTime的时间单位 */ protected TimeUnit unit = TimeUnit.SECONDS; public DefaultRedisLockSupport(String lockKey) { this.lockKey = lockKey; } public DefaultRedisLockSupport(RedissonClient redissonClient, String lockKey) { this.redissonClient = redissonClient; this.lockKey = lockKey; } public DefaultRedisLockSupport(String lockKey, long waitTime, long leaseTime) { this.lockKey = lockKey; this.waitTime = waitTime; this.leaseTime = leaseTime; } public DefaultRedisLockSupport(RedissonClient redissonClient, String lockKey, long waitTime, long leaseTime) { this.redissonClient = redissonClient; this.lockKey = lockKey; this.waitTime = waitTime; this.leaseTime = leaseTime; } public DefaultRedisLockSupport(String lockKey, long waitTime, long leaseTime, TimeUnit unit) { this.lockKey = lockKey; this.waitTime = waitTime; this.leaseTime = leaseTime; this.unit = unit; } public DefaultRedisLockSupport(RedissonClient redissonClient, String lockKey, long waitTime, long leaseTime, TimeUnit unit) { this.redissonClient = redissonClient; this.lockKey = lockKey; this.waitTime = waitTime; this.leaseTime = leaseTime; this.unit = unit; } @Override public R exec(Function function, P param) throws NotAcquiredRedisLockException { RedissonClient client = redissonClient(); RLock lock = client.getLock(lockKey); boolean obtainLock = false; try { obtainLock = lock.tryLock(waitTime, leaseTime, unit); } catch (InterruptedException e) { // ignore } if (obtainLock) { try { return function.apply(param); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } throw new NotAcquiredRedisLockException(lockKey, waitTime, unit); } @Override public R exec(NoArgFunction function) throws NotAcquiredRedisLockException { RedissonClient client = redissonClient(); RLock lock = client.getLock(lockKey); boolean obtainLock = false; try { obtainLock = lock.tryLock(waitTime, leaseTime, unit); } catch (InterruptedException e) { // ignore } if (obtainLock) { try { return function.apply(); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } throw new NotAcquiredRedisLockException(lockKey, waitTime, unit); } @Override public

void voidExec(Consumer

consumer, P param) throws NotAcquiredRedisLockException { RedissonClient client = redissonClient(); RLock lock = client.getLock(lockKey); boolean obtainLock = false; try { obtainLock = lock.tryLock(waitTime, leaseTime, unit); } catch (InterruptedException e) { // ignore } if (obtainLock) { try { consumer.accept(param); return; } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } throw new NotAcquiredRedisLockException(lockKey, waitTime, unit); } @Override public void voidExec(NoArgConsumer consumer) throws NotAcquiredRedisLockException { RedissonClient client = redissonClient(); RLock lock = client.getLock(lockKey); boolean obtainLock = false; try { obtainLock = lock.tryLock(waitTime, leaseTime, unit); } catch (InterruptedException e) { // ignore } if (obtainLock) { try { consumer.accept(); return; } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } throw new NotAcquiredRedisLockException(lockKey, waitTime, unit); } /** * 获取RedissonClient实例 * * @return RedissonClient实例 */ public RedissonClient redissonClient() { if (this.redissonClient != null) { return this.redissonClient; } if (DefaultRedisLockSupport.defaultRedissonClient != null) { return DefaultRedisLockSupport.defaultRedissonClient; } throw new IllegalStateException("There is not redissonClient available."); } /** * 是否有默认的Redisson客户端 */ public static boolean hasDefaultRedissonClient() { return DefaultRedisLockSupport.defaultRedissonClient != null; } /** * 初始化默认的Redisson客户端 * * @param redissonClient * Redisson客户端实例 */ public static void initDefaultRedissonClient(RedissonClient redissonClient) { if (DefaultRedisLockSupport.defaultRedissonClient != null && !DefaultRedisLockSupport.defaultRedissonClient.equals(redissonClient)) { throw new IllegalStateException("defaultRedissonClient already been initialized."); } synchronized (DefaultRedisLockSupport.class) { if (DefaultRedisLockSupport.defaultRedissonClient != null) { if (DefaultRedisLockSupport.defaultRedissonClient.equals(redissonClient)) { return; } throw new IllegalStateException("defaultRedissonClient already been initialized."); } DefaultRedisLockSupport.defaultRedissonClient = redissonClient; } } /** * 方法级分布式锁 */ @Target(value = ElementType.METHOD) @Retention(value = RetentionPolicy.RUNTIME) public @interface Synchronized { /** * 锁 key (支持spel) *

         *  spel表达式(注:因为本方法中用到了{@link TemplateParserContext}, 所以需要使用#{}将原生的spel包起来,形成最终的表达式)
         *  1、获取属性示例:{@code #{#param1.fieldA} }
         *  2、调用方法示例:{@code #{#param2.methodA()} }
         *  3、调用spring-bean示例:{@code #{@userService.getUsername(#userId)}},
         *     注:使用之前需要初始化bean解析器{@link SpelUtil#initBeanResolver(BeanResolver)}
         *        初始化bean解析器示例:{@code SpelUtil.initBeanResolver(new BeanFactoryResolver(applicationContext)); }
         *  4、调用静态字段示例:{@code #{T(org.springframework.core.Ordered).HIGHEST_PRECEDENCE} }
         *     注:若调用的是java.lang.包下的类,可以不指定包名
         *  5、调用静态方法示例:{@code #{T(java.util.Objects).nonNull(#returnObj)} }
         *     注:若调用的是java.lang.包下的类,可以不指定包名
         *  6、枚举示例1:{@code {T(com.ideaaedi.demo.enums.CachePrefixEnum).USER_ACCOUNT_PHONE.name()} }
         *     注:和调用静态方法是一样的
         *  7、枚举示例2:{@code #{T(com.ideaaedi.demo.enums.CachePrefixEnum).USER_ACCOUNT_PHONE.key(#user.account, #user.phone)} }
         *     注:和调用静态方法是一样的
         *  8、判断示例1:{@code #{#code == 200} }
         *  9、判断示例2:{@code #{#user == null} }
         *  10、......
         *
         *  更多spel语法见{@link SpelUtil}
         * 
*/ String lockKey(); /** 等待获取锁的最大时长 (0表示不等待, 直接获取,无论能否获取到) */ long waitTime() default NO_WAIT; /** 释放锁的最大时长 */ long leaseTime() default 3L; /** WaitTime和LeaseTime的时间单位 */ TimeUnit unit() default TimeUnit.SECONDS; } /** * {@link DefaultRedisLockSupport.Synchronized}校验器 * * @author JustryDeng * @since 2022/5/17 14:29 */ @SuppressWarnings("AlibabaAbstractMethodOrInterfaceMethodMustUseJavadoc") public interface SynchronizedValidator extends Validator> { @Override default void validate() throws ValidateException{ throw new UnsupportedOperationException(); } @Override default void validate(Supplier> supplier, Function, Boolean> function) throws ValidateException{ throw new UnsupportedOperationException(); } @Override default boolean validateAndGet(){ throw new UnsupportedOperationException(); } @Override default boolean validateAndGet(Supplier> supplier){ throw new UnsupportedOperationException(); } @Override default boolean validateAndGet(Supplier> supplier, Function, Boolean> function){ throw new UnsupportedOperationException(); } } /** * 方法级分布式锁 aop 实现 *
*
* 一般的业务需求下,分布式锁的aop优先级需要大于声明式事务@Transactional的。 *
*
* 此AOP的优先级为{@link Ordered#HIGHEST_PRECEDENCE} + 100, * 而声明式事务@Transactional的优先级在{@link org.springframework.transaction.annotation.EnableTransactionManagement#order()}控制,其默认值为 * {@link Ordered#LOWEST_PRECEDENCE}},所以此AOP的优先级是大于声明式事务@Transactional的 * * @author JustryDeng * @since 2022/5/14 10:06 */ @Slf4j @Aspect public static class SynchronizedAdvice implements BeanPostProcessor, Ordered { public static final String BEAN_NAME = "synchronizedAdviceAdvice"; /** aop order */ public static int order = Ordered.HIGHEST_PRECEDENCE + 100; /** {@link DefaultRedisLockSupport.Synchronized}校验器, 项目启动时校验 */ @Autowired(required = false) private SynchronizedValidator synchronizedValidator; /** 若redissonClient为null, 则会使用{@link DefaultRedisLockSupport#defaultRedissonClient} */ @Autowired(required = false) private RedissonClient redissonClient; @Around("@annotation(synchronizedAnno)") public Object aroundAdvice(ProceedingJoinPoint thisJoinPoint, Synchronized synchronizedAnno) throws Throwable { String lockKeySpel = synchronizedAnno.lockKey(); long waitTime = synchronizedAnno.waitTime(); long leaseTime = synchronizedAnno.leaseTime(); TimeUnit unit = synchronizedAnno.unit(); Method method = ((MethodSignature) thisJoinPoint.getSignature()).getMethod(); Object[] arguments = thisJoinPoint.getArgs(); String lockKey; if (StringUtils.isNotBlank(lockKeySpel) && lockKeySpel.contains("#")) { lockKey = SpelUtil.parseSpel(method, arguments, String.class, lockKeySpel); } else { lockKey = lockKeySpel; } log.debug("lockKeySpel -> {}, lockKey -> {}, waitTime -> {}, leaseTime -> {}, unit -> {}, ", lockKeySpel, lockKey, waitTime, leaseTime, unit); RedissonClient client = redissonClient == null ? DefaultRedisLockSupport.defaultRedissonClient : redissonClient; Objects.requireNonNull(client, "redissonClient is null. Please provide redissonClient."); RLock lock = client.getLock(lockKey); boolean obtainLock = false; try { obtainLock = lock.tryLock(waitTime, leaseTime, unit); } catch (InterruptedException e) { // ignore } if (obtainLock) { try { return thisJoinPoint.proceed(); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } throw new NotAcquiredRedisLockException(lockKey, waitTime, unit); } @Override public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException { // 校验 if (synchronizedValidator != null) { Class clazz = bean.getClass(); for (Method method : clazz.getMethods()) { Synchronized annotation = AnnotationUtils.findAnnotation(method, Synchronized.class); if (annotation != null) { synchronizedValidator.validate(() -> Pair.of(method, annotation)); } } } return bean; } @Override public int getOrder() { return SynchronizedAdvice.order; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy