原文地址html
前言
注解是在JDK1.5
以后引入的新特性位于java.lang.annotation
,注解其实就是对代码进行一种特殊的标记,这些标记能够在编译,类加载和运行时被读取,并执行相应的处理。本文主要分析如何自定义注解和注解的一些基础知识,而后在配合这AOP
在实际运用中玩出新花样。java
本文分为三部分git
- 注解分析
- 自定义注解
- 编译时注解
- 运行时注解
- 整合AOP
本来能够分为两篇文章,可是想来想去仍是写一篇。趁热打铁。github
注解分析
注解怎么运行的
想要自定义注解就要知道注解是怎么构成的,结合着项目中经常使用的注解来分析一下注解究竟是怎么工做的。web
看一下@Override
注解 其主要做用是编译时进行格式检查。点进去看一下@Override
实现。ide
点进去发现里面是空的除了两个元注解什么都没有,那么它究竟是怎么实现的呢工具
其实@Override
能够理解为是一个标签,它并无实际的逻辑处理,而实现逻辑的就是注解的用户。它本质就是一个 『标记式注解』,仅被编译器可知 。测试
举个例子你的老板让你整理一下重要的文档,可是文档太多了你确定须要把一下重要的文档给标记出来,而后你交给你老板的时候,老板会怎么作?老板固然是看到有标记的文档就去检查一下。google
结合着上面的例子使用@Override
注解的就是你,你的老板就是 JVM虚拟机,在编译的时候就是你的老板进行检查的时候,JVM发现了这个注解(标记)则就会进行处理 其处理机制主要是JVM内部处理。url
总结下来就是:
> 定义注解,扫描注解,执行逻辑
元注解
在自定义注解以前咱们要知道几个JDK为咱们提供的“元注解”,元注解就是定义注解的注解,下面看看都有什么做用。
元注解一共有四个,均可以在java.lang.annotation
下找到
- @Target
- @Retention
- @Documented
- @Inherited
@Target
@Target
注解主要用于定义注解使用的位置,被描述的注解能够用在什么地方 。@Target
的参数是ElementType枚举类,下面详解都有什么做用。
枚举😀 | 做用😊 |
---|---|
ElementType.PACKAGE | 注解用在包 |
ElementType.TYPE | 注解做用于类型(类,接口,注解,枚举) |
ElementType.ANNOTATION_TYPE | 注解做用于注解 |
ElementType.CONSTRUCTOR | 注解做用于构造方法 |
ElementType.METHOD | 注解做用于方法 |
ElementType.PARAMETER | 注解做用于方法参数 |
ElementType.FIELD | 注解做用于属性 |
ElementType.LOCAL_VARIABLE | 注解做用于局部变量 |
@Target
若是不设置范围的话默承认以做用于全部目标上面
看一下@Target
的源码
看一下里面有一个Value参数,它的返回值为ElementType[]
, ElementType就是上面的枚举类。
@Retention
@Retention注解的做用就是指定注解的生命周期。好比在编译时能够处理运行时能够处理等。它的枚举类为RetentionPolicy
枚举😀 | 做用😊 |
---|---|
RetentionPolicy.SOURCE | 源码中保留,编译期能够处理 |
RetentionPolicy.CLASS | Class文件中保留,Class加载时能够处理 |
RetentionPolicy.RUNTIME | 运行时保留,运行中能够处理 |
@Retention
的默认值为 RetentionPolicy.CLASS
即在Class加载时处理
@Retention
源码
@Documented
@Documented注解的话就比较简单,主要做用就是描述注解文档化。就是在 在生成javadoc的时候,是不包含注释的,可是若是注解被@Documented修饰,则生成的文档就包含该注解。 此注解在之后版本可能会被删除这里就不详细的看了。
@Inherited
@Inherited 注解修饰的注解时具备可继承性的,就是说咱们用 @Inherited 修饰了一个类,那么这个类的子类也会默认继承此注解。
源码
自定义注解
上面介绍了注解的元注解,那如今就开始实战自定义注解。
> GIT项目地址: https://github.com/scramblecode/project-demos
和往常套路同样先建立项目,上面是本文章的示例能够下载下来看。
首先先写一个简单的例子。而后实战在SpringBoot中使用自定义注解加拦截器获取到请求参数。
简单定义注解
这里介绍两个例子 一个是编译时注解,第二个例子是运行时注解。最后在配合着SpringBoot+AOP写一个项目中很是实用的例子
编译时注解
建立编译时注解咱们首先要建立一个依赖项目做为注解处理器。
首先先建立一个注解接口,使用IDEA建立能够选择建立注解。
在建立一个DataTest
注解,这里定义注解的目的就是若是使用了该注解在编译时打印出Hello World!
。
而后编写注解处理器,这里使用的AbstractProcessor
,本文只限简单使用,若是有机会写一篇文章研究AbstractProcessor
。
具体代码
而后须要建立META-INF文件,这里推荐使用谷歌的auto-service
能够自动生成META-INF/services/javax.annotation.processing.Processor
。
加入依赖便可
<dependency> <groupid>com.google.auto.service</groupid> <artifactid>auto-service</artifactid> <version>1.0-rc5</version> </dependency>
定义完后在你的主项目引入注解处理器。在POM文件中加入本地注解处理器的依赖
添加完成以后建立一个简单的类,而后加上@DataTest
注解
运行开始编译,就会发现控制台输出如下信息。
编译时注解能够写一些生成工具好比lombok这种生成代码的工具能够使用。
运行时注解
简单建立一个注解来获取被注解标识的名称和包路径。
首先建立注解,定义为运行时注解目标为类属性等。
使用注解
@GetClassName(value = "测试注解") public class Student { }
而后建立一个注解处理类,运行
控制台输出。
整合AOP
在Web开发中常常要输出日志,而后还有接口的运行时间。如今咱们就用自定义注解加AOP实现这种功能。
首先把项目完善一下,增长一个测试接口
而后建立log注解。
而后定义切面类
@Aspect @Component @Slf4j public class LoggerAspect { private static final Logger logger = LoggerFactory.getLogger(LoggerAspect.class); @Pointcut("@annotation(com.lqcoder.annotationdemo.annotation.OutputLog)") public void weblog(){ } @Around("weblog()") public Object around(ProceedingJoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); List<object> logArgs = Arrays.stream(point.getArgs()) .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse))) .collect(Collectors.toList()); try { logger.info("请求url={}, 请求参数={}", request.getRequestURI(), JSON.toJSONString(logArgs)); } catch (Exception e) { logger.error("请求参数获取异常", e); } Object result = point.proceed(); //执行时长(毫秒) long time = System.currentTimeMillis() - beginTime; try { logger.info("请求耗时={}ms, 返回结果={}", time, JSON.toJSONString(result)); } catch (Exception e) { logger.error("返回参数获取异常", e); } return result; } }
定义好以后重启项目,而后调用一下接口
运行结果能够看到已经生效。
本文结束。
> 本文由博客一文多发平台 OpenWrite 发布! </object>