注解的原理
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 定義該注解的生命周期
- RetentionPolicy.SOURCE :作用於源碼階段,比如常見的 @Override, @SuppressWarnings;
- RetentionPolicy.CLASS :作用於字節碼階段
- RetentionPolicy.RUNTIME :作用於運行階段
-
@Target 定義該注解的作用范圍
- ElementType.TYPE :用於注解到類,接口、枚舉類
- ElementType.FIELD:字段,包括枚舉類常量
- ElementType.METHOD:方法聲明
- ElementType.PARAMETER:參數聲明
- ElementType.CONSTRUCTOR:構造器聲明
- ElementType.LOCAL_VARIABLE :局部變量聲明
- ElementType.ANNOTATION_TYPE :用於注解聲明,即注解的注解,元注解
- ElementType.PACKAGE :包聲明
-
其他注解
- @Document 注解將生成到javadoc中
- @Deprecated 表示過時的類
- @Inherited 是否允許子類繼承該注解
- @SuppressWarnings 編譯器忽略掉無法識別的警告名
- @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; } }