小言_互联网的博客

自定义注解(Annotation)+redis组合,防止接口的重复请求

535人阅读  评论(0)

自定义注解+redis组合的使用达到接口访问限制的用法

一,前言

本文介绍如何使用自定义注解(Annotation)+redis来解决接口请求限制的方法

二,自定义注解(Annotation)

随着springboot的流行,以前基于XML的spring配置用的越来越少,JavaConfig形式的使用越来越多,类似于:


  
  1. @Configuration
  2. public class AppConfig {
  3. @Bean(name= "helloBean")
  4. public HelloWorld helloWorld() {
  5. return new HelloWorldImpl();
  6. }
  7. }

可以看出更多的是基于注解(Annotation)实现的,包括springboot的入口类**Application。Java注解不仅让我们减少了项目中XML文件,方便了维护,同时也使我们代码更简洁。那么项目中我们如何阅读注解以及如何创造自己的注解呢? 

三,注解(Annotation)说明

Java注解又称Java标注,是Java语言5.0版本开始支持加入源代码的特殊语法元数据。为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。
Java语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可以通过反射获取注解内容。在编译器生成类文件时,注解可以被嵌入到字节码中。Java虚拟机可以保留注解内容,在运行时可以获取到注解内容。

四,正式步入主题

1,自定义CacheLock注解。属性:redis 锁key的前缀;过期时间;超时时间单位;生成Key的分隔符(默认 :)


  
  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. public @interface CacheLock {
  6. /**
  7. * redis 锁key的前缀
  8. *
  9. * @return redis 锁key的前缀
  10. */
  11. String prefix() default "";
  12. /**
  13. * 过期秒数,默认为2秒
  14. *
  15. * @return 轮询锁的时间
  16. */
  17. int expire() default 2;
  18. /**
  19. * 超时时间单位
  20. *
  21. * @return
  22. */
  23. TimeUnit timeUnit() default TimeUnit.SECONDS;
  24. /**
  25. * <p>Key的分隔符(默认 :)</p>
  26. * @return String
  27. */
  28. String delimiter() default ":";
  29. }

2,自定义CacheParam注解。属性:字段名称name,获取的是你某个接口的请求参数的值


  
  1. @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. public @interface CacheParam {
  6. /**
  7. * 字段名称
  8. *
  9. * @return String
  10. */
  11. String name() default "";
  12. }

 3,创建一个类标识为一个切面供容器读取

@Around注解可以用来在调用一个具体方法前和调用后来完成一些具体的任务。

  
  1. @Log4j2
  2. @Aspect
  3. @Configuration
  4. public class LockMethodAspect {
  5. @Autowired
  6. private RedisLockHelperUtil redisLockHelper;
  7. @Autowired
  8. private CacheKeyGenerator cacheKeyGenerator;
  9. @Around( "execution(public * *(..)) && @annotation(com.common.annotation.CacheLock)")
  10. public Object interceptor(ProceedingJoinPoint pjp) {
  11. MethodSignature signature = (MethodSignature) pjp.getSignature();
  12. Method method = signature.getMethod();
  13. CacheLock lock = method.getAnnotation(CacheLock.class);
  14. if (StringUtils.isEmpty(lock.prefix())) {
  15. throw new RuntimeException( "lock key don't null...");
  16. }
  17. final String lockKey = cacheKeyGenerator.getLockKey(pjp);
  18. String value = UUID.randomUUID().toString();
  19. try {
  20. // 假设上锁成功,但是设置过期时间失效,以后拿到的都是 false
  21. final boolean success = redisLockHelper.lock(lockKey, value, lock.expire(), lock.timeUnit());
  22. if (!success) {
  23. try{
  24. throw new MyException( "操作异常");
  25. } catch (MyException e){
  26. log.error(e);
  27. Map<String, Object> result = new HashMap<String, Object>();
  28. result.put(ResponseJsonKeyEnum.CODE.key(), ResponseCodeEnum.STORAGE_FAILURE.code());
  29. result.put(ResponseJsonKeyEnum.MSG.key(), "系统数据处理中...,请稍后");
  30. return GsonUtil.GSON.toJson(result);
  31. }
  32. }
  33. try {
  34. return pjp.proceed();
  35. } catch (Throwable throwable) {
  36. throw new RuntimeException( "系统异常");
  37. }
  38. } finally {
  39. redisLockHelper.unlock(lockKey, value);
  40. }
  41. }
  42. }

4,RedisLockHelperUtil  工具类


  
  1. @Configuration
  2. @AutoConfigureAfter(RedisAutoConfiguration.class)
  3. public class RedisLockHelperUtil {
  4. private static final String DELIMITER = "-";
  5. /**
  6. * 如果要求比较高可以通过注入的方式分配
  7. */
  8. private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newScheduledThreadPool( 10);
  9. @Autowired
  10. private StringRedisTemplate stringRedisTemplate;
  11. /* public RedisLockHelper(StringRedisTemplate stringRedisTemplate) {
  12. this.stringRedisTemplate = stringRedisTemplate;
  13. }*/
  14. /**
  15. * 获取锁
  16. *
  17. * @param lockKey lockKey
  18. * @param uuid UUID
  19. * @param timeout 超时时间
  20. * @param unit 过期单位
  21. * @return true or false
  22. */
  23. public boolean lock(String lockKey, final String uuid, long timeout, final TimeUnit unit) {
  24. TimeUnit timeUnit = TimeUnit.MILLISECONDS;
  25. final long milliseconds = timeUnit.convert(timeout, TimeUnit.SECONDS);
  26. boolean success = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, (System.currentTimeMillis() + milliseconds) + DELIMITER + uuid);
  27. if (success) {
  28. stringRedisTemplate.expire(lockKey, timeout, TimeUnit.SECONDS);
  29. } else {
  30. String oldVal = stringRedisTemplate.opsForValue().getAndSet(lockKey, (System.currentTimeMillis() + milliseconds) + DELIMITER + uuid);
  31. final String[] oldValues = oldVal.split(Pattern.quote(DELIMITER));
  32. if (Long.parseLong(oldValues[ 0]) + 1 <= System.currentTimeMillis()) {
  33. return true;
  34. }
  35. }
  36. return success;
  37. }
  38. public void unlock(String lockKey, String value) {
  39. unlock(lockKey, value, 0, TimeUnit.MILLISECONDS);
  40. }
  41. /**
  42. * 延迟unlock
  43. *
  44. * @param lockKey key
  45. * @param uuid client(最好是唯一键的)
  46. * @param delayTime 延迟时间
  47. * @param unit 时间单位
  48. */
  49. public void unlock(final String lockKey, final String uuid, long delayTime, TimeUnit unit) {
  50. if (StringUtils.isEmpty(lockKey)) {
  51. return;
  52. }
  53. if (delayTime <= 0) {
  54. doUnlock(lockKey, uuid);
  55. } else {
  56. EXECUTOR_SERVICE.schedule(() -> doUnlock(lockKey, uuid), delayTime, unit);
  57. }
  58. }
  59. /**
  60. * @param lockKey key
  61. * @param uuid client(最好是唯一键的)
  62. */
  63. private void doUnlock(final String lockKey, final String uuid) {
  64. String val = stringRedisTemplate.opsForValue().get(lockKey);
  65. if (!StringUtils.isEmpty(val)){
  66. final String[] values = val.split(Pattern.quote(DELIMITER));
  67. if (values.length <= 0) {
  68. return;
  69. }
  70. if (uuid.equals(values[ 1])) {
  71. stringRedisTemplate.delete(lockKey);
  72. }
  73. }
  74. }
  75. }

5,CacheKeyGenerator 工具类,获取AOP参数,生成指定缓存Key


  
  1. import org.aspectj.lang.ProceedingJoinPoint;
  2. /**
  3. * key生成器
  4. */
  5. public interface CacheKeyGenerator {
  6. /**
  7. * 获取AOP参数,生成指定缓存Key
  8. *
  9. * @param pjp PJP
  10. * @return 缓存KEY
  11. */
  12. String getLockKey(ProceedingJoinPoint pjp);
  13. }

6,应用


  
  1. @CacheLock(prefix = "bbb")
  2. @RequestMapping(value = "/aaa", method = RequestMethod.POST)
  3. public String bbb(@Valid CccRequest cccRequest, BindingResult paramValid) throws Exception {
  4. String error = RequestParamValid.valid(paramValid);
  5. if (StringUtils.isNotEmpty(error)) {
  6. return error;
  7. }
  8. Map<String, Object> result = new HashMap<>();
  9. try {
  10. ...
  11. } catch (Exception e) {
  12. log.error(e);
  13. result.put(ResponseJsonKeyEnum.CODE.key(), ResponseCodeEnum.FAILURE.code());
  14. result.put(ResponseJsonKeyEnum.MSG.key(), ResponseCodeEnum.FAILURE.msg());
  15. }
  16. return GsonUtil.GSON.toJson(result);
  17. }

  
  1. @Setter
  2. @Getter
  3. public class CccRequest {
  4. @CacheParam(name = "id")
  5. @NotNull(message = "订单id不能为空")
  6. private Long id;
  7. //备注
  8. private String remark;
  9. }

五,完毕


转载:https://blog.csdn.net/f_15932950536/article/details/104681222
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场