@Repeatable元注解的用法

死牛胖子
• 阅读 571

在同一个元素上,标记两个相同类型的注解,会导致编译失败。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
    String value();
}

// 编译报错,不能标记多个
@Value(value = "1")
@Value(value = "2")
public class Source {}

@Repeatable 是 JDK1.8 增加的元注解,可以解决注解可以多次标记的问题。

如何声明

在注解上标记 @Repeatable 元注解,设置值为当前注解的集合注解,这个注解理论上来说没有其它的意义,仅仅是为了满足语法要求。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Values.class)
public @interface Value {
    String value();
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Values {
    Value[] value();
}

如何使用

// 编译通过
@Value(value = "1")
@Value(value = "2")
public class Source {}

如何解析

标记了 @Repeatable 的注解,在解析时与普通注解不太一样。JDK 会根据注解数量进行区别解析。

  • 如果标记一个,则解析为 @Value
  • 如果标记多个,则解析为 @Values
@Value(value = "1")
// @Value(value = "2")
public class Source {
    public static void main(String[] args) throws Exception {
        Annotation[] annotations = Source.class.getAnnotations();
        Arrays.stream(annotations).forEach(System.out::println);
    }
}

// 输出:@Value(value=1)
// 打开注释,输出:@Values(value=[@Value(value=1),@Value(value=2)])

因为 @Values 理论上是一个辅助型注解,没有实际的意义,所以在解析过程中,如果能够忽略掉,而只针对 @Value 这个我们实际使用的注解进行解析会更直观一点。

getAnnotationsByType 方法的使用

JDK 提供了 getAnnotationsByType 方法,忽略掉 @Values,收集所有的 @Value 注解作为一个数组进行返回,如果没有,则返回空数组。

@Value(value = "1")
// @Value(value = "2")
public class Source {
    public static void main(String[] args) throws Exception {
        Annotation[] annotations = Source.class.getAnnotationsByType(Value.class);
        Arrays.stream(annotations).forEach(System.out::println);
    }
}

// 输出:@Value(value=1)
// 打开注释,输出:@Value(value=1),@Value(value=2)

与 @Inherited 元注解结合使用

为 @Value 及 @Values 添加 @Inherited 元注解,使其拥有被继承的功能

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Values.class)
@Inherited
public @interface Value {
    String value();
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Values {
    Value[] value();
}

父类与子类分别声明 @Value 注解,观察是继承及重写的情况

@Value("1")    // 代码 1
// @Value("2") // 代码 2
public class Base {}

// @Value("3") // 代码 3
// @Value("4") // 代码 4
public class Child extends Base {
    public static void main(String[] args) throws Exception {
        Annotation[] annotations = Child.class.getAnnotations();
        Arrays.stream(annotations).forEach(System.out::println);
    }
}

这里需要再次强调解析的规则

  • 如果标记一个,则解析为 @Value
  • 如果标记多个,则解析为 @Values

子类解析的 @Value 注解会重写父类解析的 @Value 注解,子类解析的 @Values 注解会重写父类解析的 @Values 注解,而解析的 @Value 注解与 @Values 注解则互相不关联。

场景一:只有代码 1

父类标记了一个 @Value 注解,解析为 @Value 注解,子类继承父类

@Value(value=1)

场景二:代码 1、2

父类标记了多个 @Value 注解,解析为 @Value 注解,子类继承父类

@Values(value=[@Value(value=1),@Value(value=2)])

场景三:代码 1、3

父类与子类都只标记了一个注解,都解析为 @Value 注解,子类覆盖父类

@Value(value=3)

场景四:代码 1、2、3

父类标记了多个 @Value 注解,解析为 @Values 注解,子类标记了一个 @Value 注解,解析为 @Value 注解,@value 与 @Values 互不干扰,子类继承父类的 @Values 注解,子类自己的 @Value 注解也独自生效

@Value(value=3)
@Values(value=[@Value(value=1),@Value(value=2)])

场景五:代码 1、3、4

父类标记了一个 @Value 注解,解析为 @Value 注解,子类标记了多个 @Value 注解,解析为 @Values 注解,@value 与 @Values 互不干扰,子类继承父类的 @Value 注解,子类自己的 @Values 注解也独自生效

子类继承父类的 @Value 注解,子类自己被解析为 @Values 注解

@Value(value=1)
@Values(value=[@Value(value=3),@Value(value=4)])

场景四:代码 1、2、3、4

父类与子类都标记了多个注解,都解析为 @Values 注解,子类覆盖父类

@Values(value=[@Value(value=3),@Value(value=4)])

getAnnotationsByType 方法的使用

getAnnotations、getAnnotation、isAnnotationPresent 方法解析过程是同一套体系,getAnnotationsByType 方法的解析过程则是另外一套体系。

不管怎么组合,只要子类标记了 @Value 注解,就会覆盖父类中的标记,如果子类未标记,则会继承父类中的标记。跟 @Values 注解完全无关。

@Value("1")    // 代码 1
// @Value("2") // 代码 2
public class Base {}

@Value("3") // 代码 3
@Value("4") // 代码 4
public class Child extends Base {
    public static void main(String[] args) throws Exception {
        Annotation[] annotations = Child.class.getAnnotationsByType(Value.class);
        Arrays.stream(annotations).forEach(System.out::println);
    }
}
// 输出:@Value(value=3),@Value(value=4)
点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
JAVA注解
一、初步认识注解1.为什么学习注解?答: a.能够读懂别人写的代码,特别是框架相关的代码        b.让编程更加简洁,代码更加清晰2.注解概念?答:java提供了一种原程序中的元素关联任何信息和任何元数据的途径和方法二、java中的常见注解1.JDK自带注解:        a、@Override
Easter79 Easter79
3年前
SpringBoot自定义注解
JDK1.5起开始提供了4个元注解:@Target、@Retention、@Documented、@Inherited。何谓元注解?就是注解的注解。@Target({ElementType.PARAMETER,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@
Stella981 Stella981
3年前
JPA、Hibernate、Spring data jpa之间的关系,终于明白了
什么么是JPA?全称JavaPersistenceAPI,可以通过注解或者XML描述【对象关系表】之间的映射关系,并将实体对象持久化到数据库中。为我们提供了:1)ORM映射元数据:JPA支持XML和注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;如:@Entity、@Table、@C
Stella981 Stella981
3年前
SpringBoot自定义注解
JDK1.5起开始提供了4个元注解:@Target、@Retention、@Documented、@Inherited。何谓元注解?就是注解的注解。@Target({ElementType.PARAMETER,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@
Wesley13 Wesley13
3年前
Java元注解
1、注解数据类型注解是写在.java文件中,使用@interface作为关键字,所以注解也是Java的一种数据类型,从广泛的定义来说,Class、Interface、Enum、Annotation都属于Class类型。2、元注解在创建注解的时候,需要使用一些注解来描述自己创建的注解(即注解的注解),就是写在@interfac
Stella981 Stella981
3年前
Spring @ModelAttribute注解用法
之前项目中并自己并没有怎么使用到过@ModelAttribute这个注解,接手一个老项目的时候发现项目中大量使用@ModelAttribute这个注解,在这里就整理下这个注解常用的方式,也为自己做个记录,以免久了不用又忘记了@ModelAttribute使用大致有有两种,一种是是直接标记在方法上,一种是标记在方法的参数中,两种标记方法产生的效果也各不相
Easter79 Easter79
3年前
Spring注解@Resource和@Autowired区别对比、spring扫描的默认bean的Id、程序获取spring容器对象
\注解扫面的bean的ID问题0.前提需要明白注解扫描出来的bean的id默认是类名首字母小写,当然可以指定id:
Easter79 Easter79
3年前
Spring的注解@SuppressWarnings用法记录
@SuppressWarnings注解用法@SuppressWarnings注解主要用在取消一些编译器产生的警告对代码左侧行列的遮挡,有时候这会挡住我们断点调试时打的断点。如图所示:!(https://oscimg.oschina.net/oscnet/a7b24a067d19d030557e23c76abeaa34e93.png)
Wesley13 Wesley13
3年前
Java Annotation详解 理解和使用Annotation
系统中用到了java注解:查了一下如何使用注解,到底注解是什么;(1)创建方法:MsgTraceJavaClass在CreateNewClass中:name:输入MsgTrace;Kind:Annotation;就可以了;public@interfaceMsgTrace{String
死牛胖子 死牛胖子
4个月前
@Inherited元注解的用法
@Inherited元注解
死牛胖子
死牛胖子
Lv1
悲歌可以当泣,远望可以当归。
文章
7
粉丝
1
获赞
0