From 83546f9242edaba7c2118b5a3370f3a8eae1f99d Mon Sep 17 00:00:00 2001 From: ruying408 <1877972603@qq.com> Date: Mon, 26 Aug 2024 22:48:08 +0800 Subject: [PATCH] =?UTF-8?q?=E9=94=81=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cool/core/aop/NoRepeatSubmitAspect.java | 8 +- .../java/com/cool/core/cache/CoolCache.java | 75 +----------- .../java/com/cool/core/lock/CoolLock.java | 108 ++++++++++++++++++ 3 files changed, 113 insertions(+), 78 deletions(-) create mode 100644 src/main/java/com/cool/core/lock/CoolLock.java diff --git a/src/main/java/com/cool/core/aop/NoRepeatSubmitAspect.java b/src/main/java/com/cool/core/aop/NoRepeatSubmitAspect.java index d71d3dc..501b771 100644 --- a/src/main/java/com/cool/core/aop/NoRepeatSubmitAspect.java +++ b/src/main/java/com/cool/core/aop/NoRepeatSubmitAspect.java @@ -1,8 +1,8 @@ package com.cool.core.aop; import com.cool.core.annotation.NoRepeatSubmit; -import com.cool.core.cache.CoolCache; import com.cool.core.exception.CoolPreconditions; +import com.cool.core.lock.CoolLock; import com.cool.core.util.CoolSecurityUtil; import jakarta.servlet.http.HttpServletRequest; import java.time.Duration; @@ -20,7 +20,7 @@ import org.springframework.web.context.request.ServletRequestAttributes; @RequiredArgsConstructor public class NoRepeatSubmitAspect { - private final CoolCache coolCache; + private final CoolLock coolLock; @Around("@annotation(noRepeatSubmit)") public Object around(ProceedingJoinPoint joinPoint, NoRepeatSubmit noRepeatSubmit) throws Throwable { @@ -28,12 +28,12 @@ public class NoRepeatSubmitAspect { RequestContextHolder.getRequestAttributes())).getRequest(); String key = request.getRequestURI() + ":" + CoolSecurityUtil.getCurrentUserId(); // 加锁 - CoolPreconditions.check(!coolCache.tryLock(key, Duration.ofMillis(noRepeatSubmit.expireTime())), "请勿重复操作"); + CoolPreconditions.check(!coolLock.tryLock(key, Duration.ofMillis(noRepeatSubmit.expireTime())), "请勿重复操作"); try { return joinPoint.proceed(); } finally { // 移除锁 - coolCache.unlock(key); + coolLock.unlock(key); } } } diff --git a/src/main/java/com/cool/core/cache/CoolCache.java b/src/main/java/com/cool/core/cache/CoolCache.java index 27ddd76..aa6d381 100644 --- a/src/main/java/com/cool/core/cache/CoolCache.java +++ b/src/main/java/com/cool/core/cache/CoolCache.java @@ -7,10 +7,6 @@ import com.cool.core.util.ConvertUtil; import jakarta.annotation.PostConstruct; import java.time.Duration; import java.util.Arrays; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.cache.CacheType; @@ -46,10 +42,6 @@ public class CoolCache { final private CacheManager cacheManager; - private final Map lockMap = new ConcurrentHashMap<>(); - - private static final String LOCK_PREFIX = "lock:"; - @PostConstruct private void init() { cache = cacheManager.getCache(cacheName); @@ -63,7 +55,7 @@ public class CoolCache { /** * 数据来源 */ - public static interface ToCacheData { + public interface ToCacheData { Object apply(); } @@ -185,69 +177,4 @@ public class CoolCache { java.time.Duration.ofSeconds(ttl)); } } - - /** - * 尝试获取锁 - * - * @param key 锁的 key - * @param expireTime 锁的过期时间 - * @return 如果成功获取锁则返回 true,否则返回 false - */ - public boolean tryLock(String key, Duration expireTime) { - String lockKey = getLockKey(key); - if (type.equalsIgnoreCase(CacheType.CAFFEINE.name())) { - Lock lock = lockMap.computeIfAbsent(lockKey, k -> new ReentrantLock()); - return lock.tryLock(); - } - byte[] lockKeyBytes = lockKey.getBytes(); - // 使用 putIfAbsent 来尝试设置锁,如果成功返回 true,否则返回 false - return redisCache.putIfAbsent(cacheName, lockKeyBytes, new byte[0], expireTime) == null; - } - - /** - * 释放锁 - */ - public void unlock(String key) { - String lockKey = getLockKey(key); - if (type.equalsIgnoreCase(CacheType.CAFFEINE.name())) { - Lock lock = lockMap.get(lockKey); - if (lock != null && lock.tryLock()) { - lock.unlock(); - lockMap.remove(lockKey); - } - return; - } - redisCache.remove(cacheName, lockKey.getBytes()); - } - - /** - * 拼接锁前缀 - */ - private String getLockKey(String key) { - return LOCK_PREFIX + key; - } - - /** - * 等待锁 - * - * @param key 锁的 key - * @param expireTime 锁的过期时间 - * @return 如果成功获取锁则返回 true,否则返回 false - */ - public boolean waitForLock(String key, Duration expireTime, Duration waitTime) { - long endTime = System.currentTimeMillis() + waitTime.toMillis(); - while (System.currentTimeMillis() < endTime) { - if (tryLock(key, expireTime)) { - return true; - } - // 等待锁释放 - try { - Thread.sleep(100); // 可以根据需要调整等待时间 - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return false; - } - } - return false; - } } diff --git a/src/main/java/com/cool/core/lock/CoolLock.java b/src/main/java/com/cool/core/lock/CoolLock.java new file mode 100644 index 0000000..8ff0a80 --- /dev/null +++ b/src/main/java/com/cool/core/lock/CoolLock.java @@ -0,0 +1,108 @@ +package com.cool.core.lock; + + +import jakarta.annotation.PostConstruct; +import java.time.Duration; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.cache.CacheType; +import org.springframework.cache.CacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class CoolLock { + // 缓存类型 + @Value("${spring.cache.type}") + private String type; + + @Value("${cool.cacheName}") + private String cacheName; + + private final CacheManager cacheManager; + private RedisCacheWriter redisCache ; + + // 非redis方式时使用 + private static final Map lockMap = new ConcurrentHashMap<>(); + + private static final String LOCK_PREFIX = "lock:"; + + @PostConstruct + private void init() { + this.type = type.toLowerCase(); + if (type.equalsIgnoreCase(CacheType.REDIS.name())) { + redisCache = (RedisCacheWriter) Objects.requireNonNull(cacheManager.getCache(cacheName)) + .getNativeCache(); + } + } + /** + * 尝试获取锁 + * + * @param key 锁的 key + * @param expireTime 锁的过期时间 + * @return 如果成功获取锁则返回 true,否则返回 false + */ + public boolean tryLock(String key, Duration expireTime) { + String lockKey = getLockKey(key); + if (type.equalsIgnoreCase(CacheType.CAFFEINE.name())) { + Lock lock = lockMap.computeIfAbsent(lockKey, k -> new ReentrantLock()); + return lock.tryLock(); + } + byte[] lockKeyBytes = lockKey.getBytes(); + // 使用 putIfAbsent 来尝试设置锁,如果成功返回 true,否则返回 false + return redisCache.putIfAbsent(cacheName, lockKeyBytes, new byte[0], expireTime) == null; + } + + /** + * 释放锁 + */ + public void unlock(String key) { + String lockKey = getLockKey(key); + if (type.equalsIgnoreCase(CacheType.CAFFEINE.name())) { + Lock lock = lockMap.get(lockKey); + if (lock != null && lock.tryLock()) { + lock.unlock(); + lockMap.remove(lockKey); + } + return; + } + redisCache.remove(cacheName, lockKey.getBytes()); + } + + /** + * 拼接锁前缀 + */ + private String getLockKey(String key) { + return LOCK_PREFIX + key; + } + + /** + * 等待锁 + * + * @param key 锁的 key + * @param expireTime 锁的过期时间 + * @return 如果成功获取锁则返回 true,否则返回 false + */ + public boolean waitForLock(String key, Duration expireTime, Duration waitTime) { + long endTime = System.currentTimeMillis() + waitTime.toMillis(); + while (System.currentTimeMillis() < endTime) { + if (tryLock(key, expireTime)) { + return true; + } + // 等待锁释放 + try { + Thread.sleep(100); // 可以根据需要调整等待时间 + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return false; + } + } + return false; + } +}