1.1. 什么是注解

注解(Annotation)是Java提供的设置程序中元素的关联信息和元数据(MetaData)的方法,它是一个接口,程序可以通过反射获取指定程序中元素的注解对象,然后通过该注解对象获取注解中的元数据信息

1.2. 标准元注解:@Target、@Retention、@Documented、@Inherited

元注解(Meta-Annotation)负责注解其他注解。在Java中定义了4个标准的元注解类型@Target、@Retention、@Documented、@Inherited,用于定义不同类型的注解。

  • @Target:说明了注解所修饰的对象范围,它用于明确其修饰的目标。

注解可被用于packages、types(类、接口、枚举、注解类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(循环变量、catch参数等),详细如下:

名称 修饰目标
ElementType.TYPE 用于描述类、接口注解、枚举类型
ElementType.FIELD 用于描述域
ElementType.METHOD 用于描述方法
ElementType.PARAMETER 用于描述参数
ElementType.CONSTRUCTOR 用于描述构造器
ElementType.LOCAL_VARIABLE 用于描述局部变量
ElementType.ANNOTATION_TYPE 用于描述一个注解
ElementType.PACKAGE 用于描述包
ElementType.TYPE_PARAMETER 用于描述普通变量(JDK8)
ElementType.TYPE_USE 用于描述类型使用,能标注任何类型(JDK8)
ElementType.MODULE 用于描述模块(JDK9)
  • @Retention: 定义了该注解被保留的级别,为注解声明生命周期,它表示注解存在阶段是保留在源码(编译期),字节码(类加载)或者运行期(JVM中运行),有以下3种类型:

    • @Retention(RetentionPolicy.SOURCE):注解仅存在于源码中,在class字节码文件中不存在。
    • @Retention(RetentionPolicy.CLASS):默认的保留策略,注解会在源码、class字节码文件中存在,但运行时无法获得。
    • @Retention(RetentionPolicy.RUNTIME):在运行时有效,注解会在源码、class字节码文件中存在,在运行时可以通过反射获取到。
  • @Documented: 表明这个注解应该被javadoc工具记录,因此可以被javadoc类的工具文档化

  • @Inherited: 被@Inherited注解的父类,其子类也会继承父类的注解

1.3. Java常用的注解

  • @SuppressWarnings:描述所标注内容产生的警告,编译器会对这些警告保持静默
  • @Deprecated:描述所标注内容,是过时的,不再被建议使用

  • @Override:描述当前方法是一个重写的方法,在编译阶段对方法进行检查

  • @FunctionalInterface:Java 8 开始支持@FunctionalInterface,标识一个匿名函数或函数式接口

  • @Repeatable:被这个元注解修饰的注解可以同时作用一个对象多次,但是每次作用注解又可以代表不同的含义

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    /**
     * Indicates the <em>containing annotation type</em> for the
     * repeatable annotation type.
     * @return the containing annotation type
     */
    Class<? extends Annotation> value();
}

1.4. 通过反射来操作注解的属性

[!NOTE]

这里所讨论的获取并动态修改注解属性值,建立在 @Retention(RetentionPolicy.RUNTIM) 的基础上,这种情况下才能在运行时(runtime)通过反射机制进行操作

public class TestAnnotaionChange {

    public static void main(String... args) throws NoSuchFieldException, IllegalAccessException {
        MyAnnotation annotation = AnnotatedChangeTestPojo.class.getDeclaredField("name").getAnnotation(MyAnnotation.class);
        System.out.println(annotation.value());  // 输出:张三
        changeAnnotationFieldValue(AnnotatedChangeTestPojo.class, "name", "value", "LuckingJack");
        System.out.println(annotation.value()); // 输出:LuckingJack
    }

    /**
     * 变更指定的实体类中的成员变量上,MyAnnotation注解的属性值
     * <p>使用了JDK动态代理</p>
     *
     * @param annotatedPojo               打上{@link MyAnnotation}注解的实体类
     * @param annotatedPojoFieldName      打上{@link MyAnnotation}注解的实体类的属性名
     * @param declareAnnotationFieldName  {@link MyAnnotation}注解的属性名
     * @param declareAnnotationFieldValue {@link MyAnnotation}注解的属性值变更为
     * @author LuckinJack
     * @date 2021/3/2 14:31
     */
    public static void changeAnnotationFieldValue(@NotNull Class<?> annotatedPojo,
                                                  @NotNull String annotatedPojoFieldName,
                                                  String declareAnnotationFieldName,
                                                  Object declareAnnotationFieldValue) throws NoSuchFieldException, IllegalAccessException {
        // 获取类AnnotatedChangeTestPojo的成员变量上标记的注解MyAnnotation的实例
        MyAnnotation annotation = annotatedPojo.getDeclaredField(annotatedPojoFieldName).getAnnotation(MyAnnotation.class);
        // 获取 annotation 这个代理实例所持有的 InvocationHandler
        InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation);
        //获取私有 memberValues 属性
        Field field = invocationHandler.getClass().getDeclaredField("memberValues");
        // 因为"memberValues"这个字段是 private final 修饰,所以要打开权限
        field.setAccessible(true);
        // 获取实例的属性map
        Map<String, Object> memberValues = (Map<String, Object>) field.get(invocationHandler);
        // 修改属性值
        memberValues.put(declareAnnotationFieldName, declareAnnotationFieldValue);
    }

    @Data
    public static class AnnotatedChangeTestPojo {

        @MyAnnotation(value = "张三")
        private String name;
    }

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {

        String value() default "UNKNOWN";
    }
}

results matching ""

    No results matching ""