自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Springboot2.x AOP 實現緩存鎖,分布式鎖

開發(fā) 架構 分布式
本人深根后臺系統(tǒng)多年的經驗;用戶在網絡不好情況下; 在做表單提交時;會出現重復提交的情況;故而我們需要:做到防止表單重提。

 [[410877]]

Springboot2.x AOP 實現 緩存鎖, 分布式鎖 防止重復提交

本人深根后臺系統(tǒng)多年的經驗;用戶在網絡不好情況下; 在做表單提交時;會出現重復提交的情況;故而我們需要:做到防止表單重提

google的guave cache

  1. <dependency> 
  2.     <groupId>org.springframework.boot</groupId> 
  3.     <artifactId>spring-boot-starter-web</artifactId> 
  4. </dependency> 
  5. <dependency> 
  6.     <groupId>org.springframework.boot</groupId> 
  7.     <artifactId>spring-boot-starter-aop</artifactId> 
  8. </dependency> 
  9. <dependency> 
  10.     <groupId>com.google.guava</groupId> 
  11.     <artifactId>guava</artifactId> 
  12.     <version>21.0</version> 
  13. </dependency> 

 

注解接口

  1. package com.ouyue.xiwenapi.annotation; 
  2.  
  3. import java.lang.annotation.*; 
  4.  
  5. /** 
  6.  * @ClassName:${} 
  7.  * @Description:TODO 
  8.  * @author:xx@163.com 
  9.  * @Date
  10.  */ 
  11. @Target(ElementType.METHOD) 
  12. @Retention(RetentionPolicy.RUNTIME) 
  13. @Documented 
  14. @Inherited 
  15. public @interface GuaveLock 
  16.  
  17.     String key() default ""
  18.  
  19.     /** 
  20.      * 過期時間 TODO 由于用的 guava 暫時就忽略這屬性吧 集成 redis 需要用到 
  21.      * 
  22.      * @author fly 
  23.      */ 
  24.     int expire() default 5; 

AOP的運用

  1. package com.ouyue.xiwenapi.config; 
  2.  
  3. import com.google.common.cache.Cache; 
  4. import com.google.common.cache.CacheBuilder; 
  5. import com.ouyue.xiwenapi.annotation.GuaveLock; 
  6. import org.aspectj.lang.ProceedingJoinPoint; 
  7. import org.aspectj.lang.annotation.Around; 
  8. import org.aspectj.lang.annotation.Aspect; 
  9. import org.aspectj.lang.reflect.MethodSignature; 
  10. import org.springframework.context.annotation.Configuration; 
  11. import org.springframework.util.StringUtils; 
  12.  
  13. import java.lang.reflect.Method; 
  14. import java.util.concurrent.TimeUnit; 
  15.  
  16. /** 
  17.  * @ClassName:${} 
  18.  * @Description:TODO 
  19.  * @author:xx@163.com 
  20.  * @Date
  21.  */ 
  22. @Aspect 
  23. @Configuration 
  24. public class LockMethodAopConfigure { 
  25.     private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder() 
  26.             // 最大緩存 100 個 
  27.             .maximumSize(1000) 
  28.             // 設置寫緩存后 5 秒鐘過期 
  29.             .expireAfterWrite(5, TimeUnit.SECONDS) 
  30.             .build(); 
  31.  
  32.     @Around("execution(public * *(..)) && @annotation(com.ouyue.xiwenapi.annotation.GuaveLock)"
  33.     public Object interceptor(ProceedingJoinPoint pjp) { 
  34.         MethodSignature signature = (MethodSignature) pjp.getSignature(); 
  35.         Method method = signature.getMethod(); 
  36.         GuaveLock localLock = method.getAnnotation(GuaveLock.class); 
  37.         String key = getKey(localLock.key(), pjp.getArgs()); 
  38.         if (!StringUtils.isEmpty(key)) { 
  39.             if (CACHES.getIfPresent(key) != null) { 
  40.                 throw new RuntimeException("請勿重復請求"); 
  41.             } 
  42.             // 如果是第一次請求,就將 key 當前對象壓入緩存中 
  43.             CACHES.put(keykey); 
  44.         } 
  45.         try { 
  46.             return pjp.proceed(); 
  47.         } catch (Throwable throwable) { 
  48.             throw new RuntimeException("服務器異常"); 
  49.         } finally { 
  50.             // TODO 為了演示效果,這里就不調用 CACHES.invalidate(key); 代碼了 
  51.         } 
  52.     } 
  53.  
  54.     /** 
  55.      * key 的生成策略,如果想靈活可以寫成接口與實現類的方式(TODO 后續(xù)講解) 
  56.      * 
  57.      * @param keyExpress 表達式 
  58.      * @param args       參數  可以 采用MD5加密成一個 
  59.      * @return 生成的key 
  60.      */ 
  61.     private String getKey(String keyExpress, Object[] args) { 
  62.         for (int i = 0; i < args.length; i++) { 
  63.             keyExpress = keyExpress.replace("arg[" + i + "]", args[i].toString()); 
  64.         } 
  65.         return keyExpress; 
  66.     } 

Controller

  1. @RestController 
  2. @RequestMapping("/business"
  3. public class BusinessController { 
  4.     @GuaveLock(key = "business:arg[0]"
  5.     @GetMapping 
  6.     public String query(@RequestParam String token) { 
  7.         return "success - " + token; 
  8.     } 

上面的基本都是居于內存級別的緩存;在分布式系統(tǒng)上; 是無法滿足的;故而我們需要做到分布式系統(tǒng)中;也能使用

基于Redis 緩存鎖的實現

pom.xml

  1. <dependency> 
  2.     <groupId>org.springframework.boot</groupId> 
  3.     <artifactId>spring-boot-starter-data-redis</artifactId> 
  4. </dependency> 
  5. spring.redis.host=localhost 
  6. spring.redis.port=6379 

RedisLock

  1. prefix: 緩存中 key 的前綴
  2. expire: 過期時間,此處默認為 5 秒
  3. timeUnit: 超時單位,此處默認為秒
  4. delimiter: key 的分隔符,將不同參數值分割開來
  1. package com.ouyue.xiwenapi.annotation; 
  2.  
  3. import java.lang.annotation.*; 
  4. import java.util.concurrent.TimeUnit; 
  5.  
  6. /** 
  7.  * @ClassName:${} 
  8.  * @Description:TODO 
  9.  * @author:xx@163.com 
  10.  * @Date
  11.  */ 
  12. @Target(ElementType.METHOD) 
  13. @Retention(RetentionPolicy.RUNTIME) 
  14. @Documented 
  15. @Inherited 
  16. public @interface RedisLock { 
  17.     /** 
  18.      * redis 鎖key的前綴 
  19.      * 
  20.      * @return redis 鎖key的前綴 
  21.      */ 
  22.     String prefix() default ""
  23.  
  24.     /** 
  25.      * 過期秒數,默認為5秒 
  26.      * 
  27.      * @return 輪詢鎖的時間 
  28.      */ 
  29.     int expire() default 5; 
  30.  
  31.     /** 
  32.      * 超時時間單位 
  33.      * 
  34.      * @return 秒 
  35.      */ 
  36.     TimeUnit timeUnit() default TimeUnit.SECONDS; 
  37.  
  38.     /** 
  39.      * <p>Key的分隔符(默認 :)</p> 
  40.      * <p>生成的Key:N:SO1008:500</p> 
  41.      * 
  42.      * @return String 
  43.      */ 
  44.     String delimiter() default ":"

CacheParam 注解

  1. package com.ouyue.xiwenapi.annotation; 
  2.  
  3. import java.lang.annotation.*; 
  4.  
  5. /** 
  6.  * @ClassName:${} 
  7.  * @Description:TODO 
  8.  * @author:xx@163.com 
  9.  * @Date
  10.  */ 
  11. @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD}) 
  12. @Retention(RetentionPolicy.RUNTIME) 
  13. @Documented 
  14. @Inherited 
  15. public @interface CacheParam { 
  16.     /** 
  17.      * 字段名稱 
  18.      * 
  19.      * @return String 
  20.      */ 
  21.     String name() default ""

Key 生成策略

  1. package com.ouyue.xiwenapi.componet; 
  2.  
  3. import org.aspectj.lang.ProceedingJoinPoint; 
  4.  
  5. public interface CacheKeyGenerator { 
  6.     /** 
  7.      * 獲取AOP參數,生成指定緩存Key 
  8.      * 
  9.      * @param pjp PJP 
  10.      * @return 緩存KEY 
  11.      */ 
  12.     String getLockKey(ProceedingJoinPoint pjp); 

Key 生成策略(實現)

  1. package com.ouyue.xiwenapi.service; 
  2.  
  3. import com.ouyue.xiwenapi.annotation.CacheParam; 
  4. import com.ouyue.xiwenapi.annotation.RedisLock; 
  5. import com.ouyue.xiwenapi.componet.CacheKeyGenerator; 
  6. import org.aspectj.lang.ProceedingJoinPoint; 
  7. import org.aspectj.lang.reflect.MethodSignature; 
  8. import org.springframework.util.ReflectionUtils; 
  9. import org.springframework.util.StringUtils; 
  10.  
  11. import java.lang.annotation.Annotation; 
  12. import java.lang.reflect.Field; 
  13. import java.lang.reflect.Method; 
  14. import java.lang.reflect.Parameter; 
  15.  
  16. /** 
  17.  * @ClassName:${} 
  18.  * @Description:TODO 
  19.  * @author:xx@163.com 
  20.  * @Date
  21.  */ 
  22. public class LockKeyGenerator implements CacheKeyGenerator { 
  23.     @Override 
  24.     public String getLockKey(ProceedingJoinPoint pjp) { 
  25.         MethodSignature signature = (MethodSignature) pjp.getSignature(); 
  26.         Method method = signature.getMethod(); 
  27.         RedisLock lockAnnotation = method.getAnnotation(RedisLock.class); 
  28.         final Object[] args = pjp.getArgs(); 
  29.         final Parameter[] parameters = method.getParameters(); 
  30.         StringBuilder builder = new StringBuilder(); 
  31.         // TODO 默認解析方法里面帶 CacheParam 注解的屬性,如果沒有嘗試著解析實體對象中的 
  32.         for (int i = 0; i < parameters.length; i++) { 
  33.             final CacheParam annotation = parameters[i].getAnnotation(CacheParam.class); 
  34.             if (annotation == null) { 
  35.                 continue
  36.             } 
  37.             builder.append(lockAnnotation.delimiter()).append(args[i]); 
  38.         } 
  39.         if (StringUtils.isEmpty(builder.toString())) { 
  40.             final Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 
  41.             for (int i = 0; i < parameterAnnotations.length; i++) { 
  42.                 final Object object = args[i]; 
  43.                 final Field[] fields = object.getClass().getDeclaredFields(); 
  44.                 for (Field field : fields) { 
  45.                     final CacheParam annotation = field.getAnnotation(CacheParam.class); 
  46.                     if (annotation == null) { 
  47.                         continue
  48.                     } 
  49.                     field.setAccessible(true); 
  50.                     builder.append(lockAnnotation.delimiter()).append(ReflectionUtils.getField(field, object)); 
  51.                 } 
  52.             } 
  53.         } 
  54.         return lockAnnotation.prefix() + builder.toString(); 
  55.     } 

Lock 攔截器(AOP)

 

  1. package com.ouyue.xiwenapi.config; 
  2.  
  3. import com.ouyue.xiwenapi.annotation.RedisLock; 
  4. import com.ouyue.xiwenapi.componet.CacheKeyGenerator; 
  5. import org.aspectj.lang.ProceedingJoinPoint; 
  6. import org.aspectj.lang.annotation.Around; 
  7. import org.aspectj.lang.annotation.Aspect; 
  8. import org.aspectj.lang.reflect.MethodSignature; 
  9. import org.springframework.beans.factory.annotation.Autowired; 
  10. import org.springframework.context.annotation.Configuration; 
  11. import org.springframework.data.redis.connection.RedisStringCommands; 
  12. import org.springframework.data.redis.core.RedisCallback; 
  13. import org.springframework.data.redis.core.StringRedisTemplate; 
  14. import org.springframework.data.redis.core.types.Expiration; 
  15. import org.springframework.util.StringUtils; 
  16.  
  17. import java.lang.reflect.Method; 
  18.  
  19. /** 
  20.  * @ClassName:${} 
  21.  * @Description:TODO 
  22.  * @author:xx@163.com 
  23.  * @Date
  24.  */ 
  25. @Aspect 
  26. @Configuration 
  27. public class LockMethodInterceptor { 
  28.     @Autowired 
  29.     public LockMethodInterceptor(StringRedisTemplate lockRedisTemplate, CacheKeyGenerator cacheKeyGenerator) { 
  30.         this.lockRedisTemplate = lockRedisTemplate; 
  31.         this.cacheKeyGenerator = cacheKeyGenerator; 
  32.     } 
  33.  
  34.     private final StringRedisTemplate lockRedisTemplate; 
  35.     private final CacheKeyGenerator cacheKeyGenerator; 
  36.  
  37.  
  38.     @Around("execution(public * *(..)) && @annotation(com.ouyue.xiwenapi.annotation.RedisLock)"
  39.     public Object interceptor(ProceedingJoinPoint pjp) { 
  40.         MethodSignature signature = (MethodSignature) pjp.getSignature(); 
  41.         Method method = signature.getMethod(); 
  42.         RedisLock lock = method.getAnnotation(RedisLock.class); 
  43.         if (StringUtils.isEmpty(lock.prefix())) { 
  44.             throw new RuntimeException("lock key don't null..."); 
  45.         } 
  46.         final String lockKey = cacheKeyGenerator.getLockKey(pjp); 
  47.         try { 
  48.             // 采用原生 API 來實現分布式鎖 
  49.             final Boolean success = lockRedisTemplate.execute((RedisCallback<Boolean>) connection -> connection.set(lockKey.getBytes(), new byte[0], Expiration.from(lock.expire(), lock.timeUnit()), RedisStringCommands.SetOption.SET_IF_ABSENT)); 
  50.             if (!success) { 
  51.                 // TODO 按理來說 我們應該拋出一個自定義的 CacheLockException 異常;這里偷下懶 
  52.                 throw new RuntimeException("請勿重復請求"); 
  53.             } 
  54.             try { 
  55.                 return pjp.proceed(); 
  56.             } catch (Throwable throwable) { 
  57.                 throw new RuntimeException("系統(tǒng)異常"); 
  58.             } 
  59.         } finally { 
  60.             // TODO 如果演示的話需要注釋該代碼;實際應該放開 
  61.             // lockRedisTemplate.delete(lockKey); 
  62.         } 
  63.     } 

請求

  1. package com.ouyue.xiwenapi.controller; 
  2.  
  3. import com.ouyue.xiwenapi.annotation.CacheParam; 
  4. import com.ouyue.xiwenapi.annotation.GuaveLock; 
  5. import com.ouyue.xiwenapi.annotation.RedisLock; 
  6. import org.springframework.web.bind.annotation.GetMapping; 
  7. import org.springframework.web.bind.annotation.RequestMapping; 
  8. import org.springframework.web.bind.annotation.RequestParam; 
  9. import org.springframework.web.bind.annotation.RestController; 
  10.  
  11. /** 
  12.  * @ClassName:${} 
  13.  * @Description:TODO 
  14.  * @author:xx@163.com 
  15.  * @Date
  16.  */ 
  17.  
  18. @RestController 
  19. @RequestMapping("/business"
  20. public class BusinessController { 
  21.     @GuaveLock(key = "business:arg[0]"
  22.     @GetMapping 
  23.     public String query(@RequestParam String token) { 
  24.         return "success - " + token; 
  25.     } 
  26.  
  27.     @RedisLock(prefix = "users"
  28.     @GetMapping 
  29.     public String queryRedis(@CacheParam(name = "token") @RequestParam String token) { 
  30.         return "success - " + token; 
  31.     } 

mian 函數啟動類上;將key 生產策略函數注入

  1. @Bean 
  2. public CacheKeyGenerator cacheKeyGenerator() { 
  3.     return new LockKeyGenerator(); 

 

責任編輯:武曉燕 來源: 今日頭條
相關推薦

2023-01-13 07:39:07

2019-02-26 09:51:52

分布式鎖RedisZookeeper

2023-08-21 19:10:34

Redis分布式

2022-01-06 10:58:07

Redis數據分布式鎖

2021-10-25 10:21:59

ZK分布式鎖ZooKeeper

2024-11-28 15:11:28

2018-11-27 16:17:13

分布式Tomcat

2021-11-26 06:43:19

Java分布式

2024-10-07 10:07:31

2024-07-29 09:57:47

2021-02-28 07:49:28

Zookeeper分布式

2024-04-01 05:10:00

Redis數據庫分布式鎖

2017-01-16 14:13:37

分布式數據庫

2018-04-03 16:24:34

分布式方式

2017-04-13 10:51:09

Consul分布式

2022-04-08 08:27:08

分布式鎖系統(tǒng)

2024-01-02 13:15:00

分布式鎖RedissonRedis

2023-08-27 22:13:59

Redisson分布式緩存

2023-09-04 08:12:16

分布式鎖Springboot

2019-06-19 15:40:06

分布式鎖RedisJava
點贊
收藏

51CTO技術棧公眾號