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接口
- 通过反射可以获取自定义注解
@元注解
public @interface 注解名称{
// 属性列表
}
自定义注解
用户可以根据自己的需求定义注解
自定义支持优先级的单元测试
需求:实现一个类似junit单元测试的注解,可以批量运行某个类的全部加了注解的方法。需要支持支持自定义优先级执行,且可以不启动某个方法。
反射获取自定义注解
作用范围Class、Method、Field都支持
方法 | 说明 |
---|---|
getAnnotations( ) | 获取类上的所有注解内容 |
getAnnotation(XXX.class) | 获取到注解的内容 |
isAnnotationPresent(XXX.class) | 判断某个注解是否存在 |
注解类:Test
package com.soulboy.anno;
import java.lang.annotation.*;
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
/**
* 优先级
*
* @return
*/
int priority() default 0;
/**
* 是否要禁用
*/
boolean disable() default false;
}
在方法上使用注解:TestCase
package com.soulboy.anno;
public class TestCase {
@Test(priority = 1,disable = false)
public void testCase1(){
System.out.println("测试用例1");
}
@Test(priority = 2,disable = false)
public void testCase2(){
System.out.println("测试用例2");
}
@Test(priority = 3,disable = true)
public void testCase3(){
System.out.println("测试用例3");
}
@Test(priority = 4,disable = false)
public void testCase4(){
System.out.println("测试用例4");
}
}
基于方法上的注解元信息进行相应的操作:AnnoTest
package com.soulboy.anno;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class AnnoTest {
public static void main(String[] args) {
// 获取TestCase类实例:invoke的时候需要用
TestCase testCase = new TestCase();
// 获取TestCase类
Class<TestCase> testCaseClass = TestCase.class;
// 获取全部声明的方法
Method[] declaredMethods = testCaseClass.getDeclaredMethods();
// 定义一个List,存储全部有Test注解,且要运行的方法
List<Method> methodList = new ArrayList<>();
// 遍历方法,获取有注解且要运行
for (Method method : declaredMethods) {
if (method.isAnnotationPresent(Test.class)) {
//获取方法上的注解
Test annotation = method.getAnnotation(Test.class);
// disable不为true的才会运行
if (!annotation.disable()) {
// 要运行的方法添加到list
methodList.add(method);
}
}
}
// 根据优先级对要运行的方法进行排序, priority数字越小,优先级越高
methodList.sort(Comparator.comparingInt(obj -> obj.getAnnotation(Test.class).priority()));
// 遍历list,在invoke调用对应的方法
for (Method method : methodList) {
try {
//执行全部带有注解的方法
method.invoke(testCase);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}