自定义注解的原理是什么?如何实现自定义注解
注解的原理
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; } }
不积跬步,无以至千里;不积小流,无以成江海。