首页 > 技术文章 > 自定义注解的原理是什么?如何实现自定义注解

luao 2019-03-31 14:55 原文

注解的原理

1、注解

  注解英文称 Annotaion,是Java从1.5开始支持加入源码的特殊语法元数据,作为程序的元数据嵌入到程序当中。注解实现有一个重要的接口Annotation接口,利用@interface关键字,将所有使用该关键字的注解类都实现Annotation接口。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。

使用注解的好处:1、帮助代码编译检查,2、提高代码的识别度,比如 @override @Deprecated , 3、减少重复代码,简化代码、4、根据注解生成帮助文档,如 @Decument 等

2、元注解

  注解的基本语法:

 @Target({ElementType.METHOD, ElementType.TYPE})
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface AnnotationName{
  
 }

 

元注解就是注解的注解,用来描述注解的。

  • @Retention 定义该注解的生命周期

  1. RetentionPolicy.SOURCE :作用于源码阶段,比如常见的 @Override, @SuppressWarnings;
  2. RetentionPolicy.CLASS :作用于字节码阶段
  3. RetentionPolicy.RUNTIME :作用于运行阶段
  • @Target 定义该注解的作用范围 

  1. ElementType.TYPE :用于注解到类,接口、枚举类
  2. ElementType.FIELD:字段,包括枚举类常量
  3. ElementType.METHOD:方法声明
  4. ElementType.PARAMETER:参数声明
  5. ElementType.CONSTRUCTOR:构造器声明
  6. ElementType.LOCAL_VARIABLE :局部变量声明
  7. ElementType.ANNOTATION_TYPE :用于注解声明,即注解的注解,元注解
  8. ElementType.PACKAGE :包声明
  • 其他注解

  1. @Document 注解将生成到javadoc中
  2. @Deprecated  表示过时的类
  3. @Inherited 是否允许子类继承该注解
  4. @SuppressWarnings 编译器忽略掉无法识别的警告名
  5. @Override 标注的方法重载了父类的方法

3、自定义注解

 /**
  *  自定义注解
   *
   */
  @Target({ElementType.TYPE,ElementType.METHOD})
  @Retention(RetentionPolicy.RUNTIME)
  public @interface MyAnnotation {

      String [] value() default "";

 }

注意:规定注解里面只能使用java八大基本数据类型和String、enum、Annotation、class。

 

4、自定义注解的实现步骤和代码示例

  本示例的目的:打印出入参日志

    代码示例:

  • 第一步:定义注解接口
 import java.lang.annotation.*;
  
  /**
   * 打印出入参数日志
   */
  @Target({ElementType.METHOD})      //定义该注解的作用范围
  @Retention(RetentionPolicy.RUNTIME)//定义该注解的生命周期
  @Documented                        //注解将生成到javadoc中
  public @interface OuterService {
     /**
      * 默认打印输入参数
      * @return
      */
     boolean isInLog() default true;
 
     /**
      * 默认打印输出参数
      * @return
      */
     boolean isOutLog() default true;
 
 }
  • 第二步:实现接口
/**
 * @description: 日志拦截器
 * @author: xxx
 * @createdate: 
 * @lastdate:
 */
@Order(1000) //使用order属性,设置该类在spring容器中的加载顺序
@Aspect      //作用是把当前类标识为一个切面供容器读取
@Component   //把普通类实例化到spring容器中
public class OuterServiceAop {

  private static final Logger log = LoggerFactory.getLogger(OuterServiceAop.class);

  private static final String EXTRA_PARAM_NAME = "extra";
  private static final String TRACE_ID = "traceId";
  private static final String USER_PIN = "userPin";

  //此处定义一个通用的切点,以便下方4个通知使用
  @Pointcut("@annotation(com.jd.xxx.api.service.aop.OuterService)")
  public void serviceAop() {
  }

  @AfterThrowing("serviceAop()")
  public void logAfterThrow() {
  }

  /**
   * 正常
   * 异常
   * 显示参数名称编译的时候需要javac -parameters进行编译即可
   *
   * @param jp
   */
  @Around("serviceAop()") //环绕增强,相当于MethodInterceptor
  public Object around(ProceedingJoinPoint jp) {
    Object result = null;
    Signature signature = jp.getSignature();
    MethodSignature methodSignature = (MethodSignature) signature;
    Method method = methodSignature.getMethod();
    OuterService serviceAnnotation = method.getAnnotation(OuterService.class);
    Object[] args = jp.getArgs();
    Parameter[] parameters = method.getParameters();
    RequestContext context = initRequestContext(args, parameters);
    init(context);
    logRequestParameters(method, serviceAnnotation, args, parameters, context);
try {
      result = jp.proceed();
    } catch (MessageException e) {
      log.error("userPin[" + getPin(context) + "],
       类[{" + method.getDeclaringClass() + "}]
       方法[" + method.getName() + "],
       捕获异常]", e);
      result = ResultHelper.<Void>setResultHead(e.getCode(), e.getMessage(), e.getSubCode(), e.getSubMessage());
    } catch (Throwable e) {
      log.error("userPin[" + getPin(context) + "],
                类[" + method.getDeclaringClass() + "]
                方法[" + method.getName() + "],
                未捕获异常]", e);
      result = ResultHelper.<Void>setResultHead(
               ResultCodeEnum.INTERNAL_SERVER_ERROR.getCode() ,
               ExceptionUtil.errorMessage(e),
               ResultCodeEnum.INTERNAL_SERVER_ERROR.getCode() + "",
               ResultCodeEnum.INTERNAL_SERVER_ERROR.getDesc());
    } finally {
          logResult(result, method, serviceAnnotation, context);
          RequestContext.getCurrentContext().unset();
    }
    return result;
  }


  /**
   * 初始化的工作(将上下文中获取到的traceId,和userPin放入MDC,
   * 方便在日志中进行获取)
   *
   * @param context
   */
  private void init(RequestContext context) {
       MDC.put(CommonConstant.TRACE_ID, context.getTraceId());
       MDC.put(CommonConstant.PIN, context.getUserPin());
  }

  /**
   * 记录出参结果
   *
   * @param result
   * @param method
   * @param serviceAnnotation
   * @param context
   */
  private void logResult(Object result, 
                        Method method, 
                        OuterService serviceAnnotation, 
                 RequestContext context) {
    if (serviceAnnotation.isOutLog() && result != null) {
      String pin = context.getUserPin();
      if (pin == null) {
        pin = "空";
      }
      log.warn("类[{}]方法[{}],结果[{}]",  
                method.getDeclaringClass().toString(),
                method.getName(), result);  
    }
  }

  /**
   * 记录入参日志
   *
   * @param method
   * @param serviceAnnotation
   * @param args
   * @param parameters
   * @param context
   */
  private void logRequestParameters(Method method, 
                    OuterService serviceAnnotation,
               Object[] args,
                    Parameter[] parameters,
                    RequestContext context) {
    if (serviceAnnotation.isInLog() && parameters != null) {
      String pin = context.getUserPin();
      if (pin == null) {
        pin = "空";
      }
      StringBuilder sb = new StringBuilder("userPin[" + pin + "],
                    类[{" + method.getDeclaringClass() + "}]
                    方法[{" + method.getName() + "}]\r\n");
      for (int i = 0; i < parameters.length; i++) {
                    sb.append("[名=" + parameters[i].getName() + 
                    ", 类型=" + parameters[i].getType() + 
                    ", 值=" + (args == null ? null : args[i]) + "]\r\n ");
      }
      log.warn(sb.toString());
    }
  }

  /**
   * 初始化请求上下文
   *
   * @param args
   * @param parameters
   * @return
   */
  private RequestContext initRequestContext(Object[] args,
                Parameter[] parameters) {
    RequestContext requestContext = RequestContext.getCurrentContext();
    Map<String, Object> extraValue = setExtra(args, parameters, requestContext);
    setTraceId(requestContext, extraValue);
    Object userPin = extraValue.get(USER_PIN);
    if (userPin != null) {
      requestContext.setUserPin((String) userPin);
    }
    return requestContext;
  }


  /**
   * 设置额外参数
   *
   * @param args
   * @param parameters
   * @param requestContext
   * @return
   */
  private Map<String, Object> setExtra(Object[] args,
            Parameter[] parameters, 
        RequestContext requestContext) {
    Map<String, Object> extraValue = null;
    for (int i = 0; i < parameters.length; i++) {
      if (EXTRA_PARAM_NAME.equals(parameters[i].getName())) {
        extraValue = (Map<String, Object>) args[i];
      }
    }
    if (extraValue == null) {
      extraValue = new HashMap<>();
    }
    requestContext.setExtra(extraValue);
    return extraValue;
  }

  /**
   * 设置链路ID
   *
   * @param requestContext
   * @param extraValue
   * @return
   */

  private String setTraceId(RequestContext requestContext,
                Map<String, Object> extraValue) {
    String traceId = null;
    if (extraValue != null) {
      Object objTraceId = extraValue.get(TRACE_ID);
      if (objTraceId != null) {
        traceId = (String) objTraceId;
      }
    }
    if (traceId == null) {
      traceId = TraceUtil.generateTraceId();
    }
    requestContext.setTraceId(traceId);
    Object userPin = extraValue.get(USER_PIN);
    if (userPin != null) {
      requestContext.setUserPin((String) userPin);
    }
    return traceId;
  }

  private String getPin(RequestContext context) {
    String pin = context.getUserPin();
    if (pin == null) {
      pin = "空";
    }
    return pin;
  }
}

 

推荐阅读