乐趣区

关于java:Java自定义注解实战接口的幂等性

Java 自定义注解实战

什么是注解?

注解是写在 .java 文件中,应用 @interface 作为关键字的,所以注解也是 Java 的一种数据类型;注解从 JDK1.5开始引入的一个个性,能够对类、接口、办法、属性、办法参数等进行注解。

注解的作用

注解次要有以下四个作用:

  • 生成文档:生成 javadoc 文档,如@Documented
  • 编译查看:在编译期间对代码进行查看验证,如@Override
  • 编译时解决:编译时对代码进行特定的解决,如@Data
  • 运行时解决:运行时对代码进行特定的解决,如@Slf4j

注解与反射

定义注解后,咱们能够通过反射 java.lang.reflect 机制获取注解的内容。注:只有当注解的应用范畴定义为 Runtime 能力通过反射获取。

以下为具体接口 API:

办法阐明 办法
获取某个类型的注解 public <A extends Annotation> A getAnnotation(Class<A> annotationClass);
获取所有注解(包含父类中被 Inherited 润饰的注解) public Annotation[] getAnnotations();
获取申明的注解(不包含父类中被 Inherited 润饰的注解) public Annotation[] getDeclaredAnnotations();
判断某个对象上是否被某个注解进行标注 public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
获取某个类申明的所有字段 public Field[] getDeclaredFields() throws SecurityException;
获取某个办法 public Method getMethod(String name, Class<?>... parameterTypes);

自定义注解实战

联合 AOP 能够进行权限管制,日志输入及幂等性校验等;

以幂等性 1 校验举例子:

第一步:定义注解

package com.wedjg.cloud.finance.aspect.idem;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableIdem {
    /**
     * 幂等工夫距离(秒)* @return
     */
    int expired() default 3;}

第二步:应用注解

@PostMapping("test")
@EnableIdem
public CommonResult test(@RequestBody DemoDTO dto) {financeService.test(dto);
    return CommonResult.success(true);
}

第三步:编写切面

@Aspect
@Component
public class IdempotentAspect {public static final Log log = LogFactory.get();

    public static final String KEY = "Idem|";

    @Before("@annotation(com.wedjg.cloud.aspect.idem.EnableIdem)")
    public void handle(JoinPoint joinPoint) throws Exception{
        // 获取参数值
        Object[] args = joinPoint.getArgs();
        // 结构缓存的 key
        // 此处应用的是办法门路 + 参数的 hashcode 作为 key,在个别情况下有可能会反复
        // 在理论应用时应依据业务调整
        StringBuilder sb = new StringBuilder();
        for (Object arg : args) {sb.append(Convert.toStr(arg));
        }
        String methodPath = joinPoint.getSignature().getDeclaringTypeName() + CharUtil.DOT + joinPoint.getSignature().getName();
        String redisKey = KEY + methodPath + sb.toString().hashCode();

        // 将 key 存到 redis 中。要留神保障 setnx 和 expired 的原子性;// 若 key 已存在,阐明在 expired()的工夫范畴内曾经申请过。以后申请为反复申请;// 若 key 不存在,则把以后 key 设置到 redis 中,并设置过期工夫。// 获取注解的信息
        EnableIdem enableIdem = this.getDeclaredAnnotation(joinPoint);
        if(JedisUtil.setnxAndExpire(redisKey, enableIdem.expired(), "1") == 0L) {throw new BaseException("操作过于频繁,请稍后再试!");
        }
    }

    /**
     * 获取办法中申明的注解
     *
     * @param joinPoint
     * @return
     * @throws NoSuchMethodException
     */
    public EnableIdem getDeclaredAnnotation(JoinPoint joinPoint) throws NoSuchMethodException {
        // 获取办法名
        String methodName = joinPoint.getSignature().getName();
        // 反射获取指标类
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 拿到办法对应的参数类型
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        // 依据类、办法、参数类型(重载)获取到办法的具体信息
        Method objMethod = targetClass.getMethod(methodName, parameterTypes);
        // 拿到办法定义的注解信息
        EnableIdem annotation = objMethod.getDeclaredAnnotation(EnableIdem.class);
        // 返回
        return annotation;
    }
}

参考文章

  • Java 自定义注解 Annotation 详解
  • Java 根底 – 注解机制详解
  • Redis 同时操作 setnx 和 expire

  1. 用户对于同一操作发动的一次申请或者屡次申请的后果是统一的,不会因为屡次点击而产生了副作用 ↩
退出移动版