1、注解概述

1.1、注解编写格式

  1. public @interface 注解名称{
  2. 属性列表;
  3. }

1.2、注解分类

  1. 1、注解大致分为三类:自定义注解、JDK内置注解、第三方框架提供的注解
  2. 1.1、自定义注解:我们自己写的注解,比如:@UserLog
  3. 1.2JDK内置注解:
  4. 1.2.1、原生注解,比如:@Overried@Deprecated@FunctionalInterface@SuppressWarnings(关闭不当的编译器警告信息)等基本注解,大多数用于「标记」和「检查」。
  5. 1.2.2、元注解:用来修饰注解的。常用的元Annotation@Retention@Target@Document@Inherited
  6. 1.3、第三方框架定义的注解,比如:SpringMVC@Controller

1.2.1、JDK内置注解之标准注解

  1. 1@Override: 『标记式注解』,只能作用于方法之上,仅被编译器可知(编译器在对 java 文件进行编译成字节码的过程中,一旦检测到某个方法上被修饰了该注解,就会去匹对父类中是否具有一个同样方法签名的函数,如果不是,自然不能通过编译。),编译结束后将被丢弃。
  2. 2@Deprecated:『标记式注解』,永久存在,可以修饰所有的类型,作用是,标记当前的类或者方法或者字段等已经不再被推荐使用了,可能下一次的 JDK 版本就会删除。
  3. 3@FunctionalInterface:接口可以接受lambda表达式作为右值,此类接口又叫函数式接口,其规定的修饰接口只能有一个抽象的方法(不包括静态方法和替代,专有方法)。
  4. 4@SuppressWarnings:主要用来压制 java 的警告,它有一个 value 属性需要你主动的传值,该 value 代表的就是需要被压制的警告类型

1.2.2、JDK内置注解之元注解

  1. 1@Target 表示该注解用于什么地方,可能的值在枚举类 ElemenetType 中,包括:
  2. ElemenetType.CONSTRUCTOR-----------------------------允许作用在构造器上
  3. ElemenetType.FIELD ----------------------------------允许作用在属性字段上
  4. ElemenetType.LOCAL_VARIABLE------------------------- 允许作用在本地局部变量上
  5. ElemenetType.METHOD ---------------------------------允许作用在方法上
  6. ElemenetType.PACKAGE --------------------------------允许作用在包上
  7. ElemenetType.PARAMETER ------------------------------允许作用在方法参数上
  8. ElemenetType.TYPE----------------------------------- 允许被修饰的注解作用在类、接口和枚举上
  9. ElementType.ANNOTATION_TYPE-----------------------------------允许作用在注解上
  10. 2@Retention 表示在什么级别保存该注解信息。可选的参数值在枚举类型 RetentionPolicy 中,包括:
  11. RetentionPolicy.SOURCE-------------注解仅保留在源代码级别中,将被编译器丢弃所忽略掉【对应.java
  12. RetentionPolicy.CLASS -------------注解在class文件中可用,但会被JVM丢弃 【对应.class
  13. RetentionPolicy.RUNTIME ---------JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。【对应JVM
  14. 3@Documented 将此注解包含在 javadoc ,它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。相当与@see,@param 等。
  15. 4@Inherited 允许子类继承父类中的注解。
  16. 5Java8新提供元注解-示例
  17. 5.1@Repeatable (Java8): 允许在同一申明类型(类,属性,或方法)的多次使用同一个注解。
  18. 5.2 @Native (Java8) 使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。

1.3、注解使用位置

  1. 注解常常出现在类、方法、成员变量、形参位置,当然还有其他位置。

1.4、注解的作用

  1. 1、注解是一种方便程序识别的一种标签,为当前读取该注解的程序提供判断依据及少量附加信息。
  2. 1.1、比如:程序只要读到加了@Test的方法,就知道该方法是待测试方法;又比如@Before注解,程序看到这个注解,就知道该方法要放在@Test方法之前执行。

1.5、注解的级别

  1. 注解和类、接口、枚举是同一级别的。

1.6、注解的本质

  1. 1、所有的注解类型都继承自这个普通的接口(Annotation)
  2. 2@Override注解源代码
  3. @Target(ElementType.METHOD)
  4. @Retention(RetentionPolicy.SOURCE)
  5. public @interface Override {
  6. }
  7. ==> 反编译之后,可得到
  8. public interface Override extends Annotation{
  9. }

1.6.1、注解与反射

  1. 1、虚拟机规范定义了一系列和注解相关的属性表,也就是说,无论是字段、方法或是类本身,如果被注解修饰了,就可以被写进字节码文件。属性表有以下几种:
  2. RuntimeVisibleAnnotations:运行时可见的注解
  3. RuntimeInVisibleAnnotations:运行时不可见的注解
  4. RuntimeVisibleParameterAnnotations:运行时可见的方法参数注解
  5. RuntimeInVisibleParameterAnnotations:运行时不可见的方法参数注解
  6. AnnotationDefault:注解类元素的默认值
  7. 2、对于一个类或者接口来说,Class 类中提供了以下一些方法用于反射注解。
  8. 2.1AnnotatedElement 接口是所有程序元素(ClassMethodConstructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的方法来访问Annotation信息。相关接口:
  9. 2.1.1、判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false。注意:此方法会忽略注解对应的注解容器。
  10. boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)
  11. 2.1.2、返回该程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null
  12. <T extends Annotation> T getAnnotation(Class<T> annotationClass)
  13. 2.1.3、返回该程序元素上存在的所有注解,若没有注解,返回长度为0的数组。
  14. Annotation[] getAnnotations()
  15. 2.1.4、返回该程序元素上存在的、指定类型的注解数组。没有注解对应类型的注解时,返回长度为0的数组。该方法的调用者可以随意修改返回的数组,而不会对其他调用者返回的数组产生任何影响。【getAnnotationsByType会检测注解对应的重复注解容器。若程序元素为类,当前类上找不到注解,且该注解为可继承的,则会去父类上检测对应的注解。】
  16. <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)
  17. 2.1.5、返回直接存在于此元素上的所有注解。该方法将忽略继承的注释。如果没有注释直接存在于此元素上,则返回null
  18. <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)
  19. 2.1.6、返回直接存在于此元素上的所有注解。该方法将忽略继承的注释。
  20. <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)
  21. 2.1.7、返回直接存在于此元素上的所有注解及注解对应的重复注解容器。该方法将忽略继承的注解。如果没有注释直接存在于此元素上,则返回长度为零的一个数组。该方法的调用者可以随意修改返回的数组,而不会对其他调用者返回的数组产生任何影响。
  22. Annotation[] getDeclaredAnnotations()
  23. 3、示例demo
  24. 3.1、自定义注解类
  25. @Retention(RetentionPolicy.RUNTIME)
  26. @Target(ElementType.FIELD,ElementType.METHOD)
  27. @Documented
  28. @Service
  29. public @interface SelfDemo {
  30. String value();
  31. }
  32. 3.2main方法测试
  33. public class TestDemo{
  34. public static void main(String[] args) throws NoSuchMethodException {
  35. Class classs = TestDemo.class;
  36. Method method = classs.getMethod("main", String[].class);
  37. /**
  38. 通过方法名返回注解属性值: JDK 是通过动态代理机制生成一个实现我们注(接口)的代理类。
  39. -- 代理类实现接口 Hello 并重写其所有方法,包括 value 方法以及接口 Hello 从 Annotation 接口继承而来的方法。
  40. -- AnnotationInvocationHandler 是 JAVA 中专门用于处理注解的 Handler
  41. -- 当你进行反射的时候,虚拟机将所有生命周期在 RUNTIME 的注解取出来放到一个 map 中,并创建一个 AnnotationInvocationHandler 实例,把这个 map 传递给它。最后,虚拟机将采用 JDK 动态代理机制生成一个目标注解的代理类,并初始化好处理器。
  42. */
  43. SelfDemo selfDemo = method.getAnnotation(SelfDemo.class);
  44. System.out.println(selfDemo.value());
  45. }
  46. }
  47. 3.3demo总结
  48. 3.3.1、通过方法名返回注解属性值: JDK 是通过动态代理机制生成一个实现我们注(接口)的代理类。
  49. -- 代理类实现接口 Hello 并重写其所有方法,包括 value 方法以及接口 Hello Annotation 接口继承而来的方法。
  50. -- AnnotationInvocationHandler JAVA 中专门用于处理注解的 Handler
  51. -- 当你进行反射的时候,虚拟机将所有生命周期在 RUNTIME 的注解取出来放到一个 map 中,并创建一个 AnnotationInvocationHandler 实例,把这个 map 传递给它。最后,虚拟机将采用 JDK 动态代理机制生成一个目标注解的代理类,并初始化好处理器。
  52. 4、注解需要三要素:定义、使用、读取并执行

1.6.2、自定义注解示例-demo

  1. ********* 注解需要三要素:定义、使用、读取并执行 **********
  2. 1、定义注解
  3. @Retention(RetentionPolicy.RUNTIME)
  4. public @interface MyAnnotation {
  5. String getValue() default "no description";
  6. }
  7. 2、使用注解
  8. @MyAnnotation(getValue = "annotation on class")
  9. public class Demo {
  10. @MyAnnotation(getValue = "annotation on field")
  11. public String name;
  12. @MyAnnotation(getValue = "annotation on method")
  13. public void hello() {}
  14. @MyAnnotation() // 故意不指定getValue
  15. public void defaultMethod() {}
  16. }
  17. 3、测试
  18. public class AnnotationTest {
  19. public static void main(String[] args) throws Exception {
  20. // 获取类上的注解
  21. Class<Demo> clazz = Demo.class;
  22. MyAnnotation annotationOnClass = clazz.getAnnotation(MyAnnotation.class);
  23. System.out.println(annotationOnClass.getValue());
  24. // 获取成员变量上的注解
  25. Field name = clazz.getField("name");
  26. MyAnnotation annotationOnField = name.getAnnotation(MyAnnotation.class);
  27. System.out.println(annotationOnField.getValue());
  28. // 获取hello方法上的注解
  29. Method hello = clazz.getMethod("hello", (Class<?>[]) null);
  30. MyAnnotation annotationOnMethod = hello.getAnnotation(MyAnnotation.class);
  31. System.out.println(annotationOnMethod.getValue());
  32. // 获取defaultMethod方法上的注解
  33. Method defaultMethod = clazz.getMethod("defaultMethod", (Class<?>[]) null);
  34. MyAnnotation annotationOnDefaultMethod = defaultMethod.getAnnotation(MyAnnotation.class);
  35. System.out.println(annotationOnDefaultMethod.getValue());
  36. }
  37. }

1.6.3、@Data注解分析

  1. 1Lombok本质上就是一个实现了"JSR 269 API"的程序。在使用javac的过程中,它产生作用的具体流程如下:
  2. 1.1javac对源代码进行分析,生成了一棵抽象语法树(AST)。
  3. 1.2、运行过程中调用实现了"JSR 269 API"Lombok程序,此时Lombok就对第一步骤得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改该语法树(AST),增加gettersetter方法定义的相应树节点。
  4. 1.3javac使用修改后的抽象语法树(AST)生成字节码文件,即给class增加新的节点(代码块)。
  5. 2Lombok插件的一些常见注解用法:
  6. @Data 注在类上,提供类的getsetequalshashCodecanEqualtoString方法
  7.   @AllArgsConstructor 注在类上,提供类的全参构造
  8.   @NoArgsConstructor 注在类上,提供类的无参构造
  9.   @Setter 注在属性上,提供 set 方法
  10.   @Getter 注在属性上,提供 get 方法
  11.   @EqualsAndHashCode 注在类上,提供对应的 equals hashCode 方法
  12.   @Log4j/@Slf4j 注在类上,提供对应的 Logger 对象,变量名为 log
  13. 3

1.6.4.1、ASR-269基本介绍

  1. [https://www.yuque.com/moercheng/eig6e7/pfnm23](https://www.yuque.com/moercheng/eig6e7/pfnm23)

1.6.4、自定义注解与Spring AOP结合使用-示例

  1. 1、前序:在Spring AOP的逻辑处理中,判断是否带有自定义注解,如果有则将监控的逻辑写在方法的前后。这样,只要在方法上加上我的注解,那就可以有对方法监控的效果(RTQPSERROR)。
  2. 2demo
  3. 2.1、自定义注解
  4. @Retention(RetentionPolicy.RUNTIME)
  5. @Target(ElementType.FIELD,ElementType.METHOD)
  6. @Documented
  7. @Service
  8. public @interface SelfLimitDemo {
  9. //允许访问次数属性,默认值:MAX_VALUE
  10. int count() default Integer.MAX_VALUE;
  11. //访问次数间隔,单位毫秒,默认值:一分钟
  12. int time() default 60;
  13. }
  14. 2.2、自定义切面
  15. @Aspect
  16. public class LimitContract{
  17. @Before("within(@xxx.xxx.xxx) && @annotation(limit)")
  18. public void limit(Point ponit, SelfLimitDemo limitDemo){
  19. //业务处理
  20. }
  21. }
  22. 2.3、结合使用
  23. @SelfLimitDemo(count=5,time=2)
  24. @RequestMapping(value="/echo", method = RequestMethod.GET)
  25. @ResponseBody
  26. public Map echo(HttpServletRequest request){
  27. Map<String,String> params = new HashMap<>();
  28. params.put("name","liyan");
  29. return params;
  30. }

1.7、注解实现原理[编译时期注解处理]

1.7.1、注解处理器(Annotation Processor)

  1. 1、注解处理器(Annotation Processor)是javac内置的一个用于编译时扫描和处理注解(Annotation)的工具。
  2. 2、通过Annotation Processor可以获取到注解和被注解对象的相关信息,然后根据注解自动生成Java代码,省去了手动编写,提高了编码效率。
  3. 3、使用注解处理器先要了解AbstractProcessor类,这个类是一个抽象类,有四个核心方法。

1.7.2、AbstractProcessor

  1. 1、.init(ProcessingEnvironment env): 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements, TypesFiler。后面我们将看到详细的内容。
  2. 2、.process(Set<? extends TypeElement> annotations, RoundEnvironment env): 这相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素。后面我们将看到详细的内容。
  3. 3、.getSupportedAnnotationTypes(): 这里你必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,你在这里定义你的注解处理器注册到哪些注解上。
  4. 4、.getSupportedSourceVersion(): 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()。然而,如果你有足够的理由只支持Java 6的话,你也可以返回SourceVersion.RELEASE_6

1.7.3、注册自定义处理器

  1. 1、创建一个java library,其gradle配置如下:
  2. apply plugin: 'java'
  3. targetCompatibility = '1.7'
  4. sourceCompatibility = '1.7'
  5. dependencies {
  6. compile fileTree(include: ['*.jar'], dir: 'libs')
  7. compile 'com.google.auto.service:auto-service:1.0-rc3'
  8. }
  9. 2、自定义Annotation Processor继承于AbstractProcessor
  10. /**
  11. 1、自定义Annotation Processor继承于AbstractProcessor
  12. 2、@AutoService向javac注册我们这个自定义的注解处理器
  13. 2.1、主要是用来生成
  14. META-INF/services/javax.annotation.processing.Processor文件的。如果不加上这个注解,那么,你需要自己进行手动配置进行注册,
  15. 3、具体手动注册方法如下:
  16. 3.1、创建一个
  17. META-INF/services/javax.annotation.processing.Processor文件,
  18. 其内容是一系列的自定义注解处理器完整有效类名集合,以换行切割:
  19. com.example.MyProcessor
  20. com.foo.OtherProcessor
  21. 3.2、将自定义注解处理器和
  22. META-INF/services/javax.annotation.processing.Processor打包成一个.jar文件。
  23. 3.2.1、示例:
  24. MyProcessor.jar
  25. - com
  26. - example
  27. - MyProcessor.class
  28. - META-INF
  29. - services
  30. - javax.annotation.processing.Processor
  31. */
  32. @AutoService(Processor.class)
  33. public class MyProcessor extends AbstractProcessor {
  34. @Override
  35. public synchronized void init(ProcessingEnvironment env){
  36. }
  37. @Override
  38. public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment roundEnv) { }
  39. @Override
  40. public Set<String> getSupportedAnnotationTypes() {
  41. }
  42. @Override
  43. public SourceVersion getSupportedSourceVersion() {
  44. }
  45. }

1.7.4、资料

https://blog.csdn.net/qq_20009015/article/details/106038023 https://www.race604.com/annotation-processing/

2、学习资料

https://www.pdai.tech/md/java/basic/java-basic-x-annotation.html