目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

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自带的注解,那么你这个注解在引用的地方就只能使用一次。

image.png

声明注解

@interface 相当于java声明类的 class

  • 用来声明一个注解,可以通过default来声明参数的默认值
  • 自定义注解时,自动继承了java.lang.annotation.Annotation接口
  • 通过反射可以获取自定义注解
@元注解
public @interface 注解名称{
	// 属性列表
}

自定义注解

用户可以根据自己的需求定义注解image.png

自定义支持优先级的单元测试

需求:实现一个类似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);
            }
        }
    }
}

作者:Soulboy