Annotation
annotation
- 注解是在 JDK5 时引入的新特性,其实就是代码里的特殊标记
- 注解也被称为元数据,是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用
- 允许开发人员在不改变源代码的情况下,在源代码中添加一些元数据
- 以便让编译器或者其他工具可以读取这些元数据,从而实现更高级的功能
应用场景
- 生成文档
- 编译时进行格式检查,如 @Override
- 替代配置文件功能,比如 Spring 的注解
- 和反射组合应用,自己封装框架组件
Java 自带的标准注解
这些注解后编译器就会进行检查
- @Override 标记覆写父类的方
- @Deprecated 标记被修饰的类或类成员、类方法已废弃、过时
- @SuppressWarnings 用于关闭对类、方法、成员编译时产生的特定警告,
元注解
用于定义注解的注解
元注解也是Java自带的标准注解,只不过用于修饰注解,比较特殊。
@Target:表示该注解用于什么地方
- ElementType.CONSTRJCTOR 用在构造器
- ElementType.FIELD 用于描述域-属性上
- ElementType.METHOD 用在方法上
- ElementType.TYPE 用在类或接口上
- ElementType.PACKAGE 用于描述包
@Retention: 表示在什么级别保存该注解信息
- RetentionPolicy.SOURCE 保留到源码上
- RetentionPolicy.CLASS 保留到字节码上
- RetentionPolicy.RUNTIME 保留到虚拟机运行时(最多,可通过反射获取)
@Documented: 将此注解包含在javadoc中
@Inherited: 是否允许子类继承父类中的注解
@Repeatable: 注解是用来标注一个注解在同一个地方可重复使用的一个注解,比如说你定义了一个注解,如果你的注解没有标记@Repeatable这个JDK自带的注解,那么你这个注解在引用的地方就只能使用一次。
声明注解
@interface
相当于 Java 声明类的 class
- 用来声明一个注解,可以通过 default 来声明参数的默认值
- 自定义注解时,自动继承了 java.lang.annotation.Annotation 接口
- 通过反射可以获取自定义注解
1@元注解
2public @interface 注解名称{
3 // 属性列表
4}
自定义注解
用户可以根据自己的需求定义注解
自定义支持优先级的单元测试
需求:实现一个类似junit单元测试的注解,可以批量运行某个类的全部加了注解的方法。需要支持支持自定义优先级执行,且可以不启动某个方法。
反射获取自定义注解
作用范围Class、Method、Field都支持
方法 | 说明 |
---|---|
getAnnotations( ) | 获取类上的所有注解内容 |
getAnnotation(XXX.class) | 获取到注解的内容 |
isAnnotationPresent(XXX.class) | 判断某个注解是否存在 |
注解类:Test
1package com.soulboy.anno;
2
3import java.lang.annotation.*;
4
5@Inherited
6@Documented
7@Target(ElementType.METHOD)
8@Retention(RetentionPolicy.RUNTIME)
9public @interface Test {
10
11 /**
12 * 优先级
13 *
14 * @return
15 */
16 int priority() default 0;
17
18 /**
19 * 是否要禁用
20 */
21 boolean disable() default false;
22
23}
在方法上使用注解:TestCase
1package com.soulboy.anno;
2
3public class TestCase {
4 @Test(priority = 1,disable = false)
5 public void testCase1(){
6 System.out.println("测试用例1");
7 }
8 @Test(priority = 2,disable = false)
9 public void testCase2(){
10 System.out.println("测试用例2");
11 }
12
13 @Test(priority = 3,disable = true)
14 public void testCase3(){
15 System.out.println("测试用例3");
16 }
17 @Test(priority = 4,disable = false)
18 public void testCase4(){
19 System.out.println("测试用例4");
20 }
21}
基于方法上的注解元信息进行相应的操作:AnnoTest
1package com.soulboy.anno;
2
3import java.lang.reflect.InvocationTargetException;
4import java.lang.reflect.Method;
5import java.util.ArrayList;
6import java.util.Comparator;
7import java.util.List;
8
9public class AnnoTest {
10 public static void main(String[] args) {
11 // 获取TestCase类实例:invoke的时候需要用
12 TestCase testCase = new TestCase();
13 // 获取TestCase类
14 Class<TestCase> testCaseClass = TestCase.class;
15 // 获取全部声明的方法
16 Method[] declaredMethods = testCaseClass.getDeclaredMethods();
17
18 // 定义一个List,存储全部有Test注解,且要运行的方法
19 List<Method> methodList = new ArrayList<>();
20
21 // 遍历方法,获取有注解且要运行
22 for (Method method : declaredMethods) {
23 if (method.isAnnotationPresent(Test.class)) {
24 //获取方法上的注解
25 Test annotation = method.getAnnotation(Test.class);
26 // disable不为true的才会运行
27 if (!annotation.disable()) {
28 // 要运行的方法添加到list
29 methodList.add(method);
30 }
31 }
32 }
33 // 根据优先级对要运行的方法进行排序, priority数字越小,优先级越高
34 methodList.sort(Comparator.comparingInt(obj -> obj.getAnnotation(Test.class).priority()));
35
36 // 遍历list,在invoke调用对应的方法
37 for (Method method : methodList) {
38 try {
39 //执行全部带有注解的方法
40 method.invoke(testCase);
41 } catch (IllegalAccessException e) {
42 throw new RuntimeException(e);
43 } catch (InvocationTargetException e) {
44 throw new RuntimeException(e);
45 }
46 }
47 }
48}