自定義注解的原理是什么?如何實現自定義注解


注解的原理

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;
  }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM