Java的@Repeatable注解使用详细解析
作者:小徐也要努力鸭
1 前言
java8新增了注解@Repeatable,在hibernate-validator的源码注解如@MAX、@NotNull等中,有@Repeatable注解的使用,源码示例如下:
/* * Jakarta Bean Validation API * * License: Apache License, Version 2.0 * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>. */ package javax.validation.constraints; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.CONSTRUCTOR; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; import javax.validation.constraints.NotNull.List; /** * The annotated element must not be {@code null}. * Accepts any type. * * @author Emmanuel Bernard */ @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Repeatable(List.class) @Documented @Constraint(validatedBy = { }) public @interface NotNull { String message() default "{javax.validation.constraints.NotNull.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; /** * Defines several {@link NotNull} annotations on the same element. * * @see javax.validation.constraints.NotNull */ @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Documented @interface List { NotNull[] value(); } }
2 使用
参考hibernate-validator注解,使用@Repeatable:
2.1 新建注解RepeaDemo
暂时不添加@Repeatable:
package com.xiaoxu.tool.demo; import java.lang.annotation.*; @Target(value = {ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RepeaDemo { String id(); String name(); }
新建demo类:
package com.xiaoxu.tool.demo; import java.lang.reflect.Field; import java.util.Arrays; /** * @author xiaoxu * @date 2022-05-17 * spring_boot:com.xiaoxu.tool.demo.TestRepeatable */ public class TestRepeatable { @RepeaDemo(id = "1001",name = "荔枝") private String fruit1; @RepeaDemo(id = "1002",name = "葡萄") private String fruit2; public static void demo1(){ Class<?> a = TestRepeatable.class; Field[] fruits = a.getDeclaredFields(); Arrays.stream(fruits).forEach(f->{ if(f.isAnnotationPresent(RepeaDemo.class)){ RepeaDemo anno = f.getAnnotation(RepeaDemo.class); String id = anno.id(); String name = anno.name(); System.out.printf("水果id:%s,水果名称:%s%n",id,name); } }); } public static void main(String[] args) { demo1(); } }
执行结果如下:
水果id:1001,水果名称:荔枝
水果id:1002,水果名称:葡萄
但是问题是,如果希望在字段上增加多个注解,那么会提示Duplicate annotation.错误:
于是考虑新增一个RepeaDemos注解,用于存储@RepeaDemo注解数组,就可以达到多个注解的使用了,如下:
package com.xiaoxu.tool.demo; import java.lang.annotation.*; @Target(value = {ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RepeaDemos { RepeaDemo[] repeaDemos(); }
修改TestRepeatable代码:
package com.xiaoxu.tool.demo; import java.lang.reflect.Field; import java.util.Arrays; /** * @author xiaoxu * @date 2022-05-17 * spring_boot:com.xiaoxu.tool.demo.TestRepeatable */ public class TestRepeatable { @RepeaDemos(repeaDemos = { @RepeaDemo(id = "1001",name = "荔枝"), @RepeaDemo(id = "1003",name = "沃柑") }) private String fruit1; @RepeaDemo(id = "1002",name = "葡萄") private String fruit2; public static void demo1(){ Class<?> a = TestRepeatable.class; Field[] fruits = a.getDeclaredFields(); Arrays.stream(fruits).forEach(f->{ if(f.isAnnotationPresent(RepeaDemo.class)){ RepeaDemo anno = f.getAnnotation(RepeaDemo.class); String id = anno.id(); String name = anno.name(); System.out.printf("水果id:%s,水果名称:%s%n",id,name); } }); } public static void demo2(){ Class<?> a = TestRepeatable.class; Field[] fruits = a.getDeclaredFields(); Arrays.stream(fruits).forEach(f->{ if(f.isAnnotationPresent(RepeaDemos.class)){ RepeaDemos anno = f.getAnnotation(RepeaDemos.class); RepeaDemo[] res= anno.repeaDemos(); Arrays.stream(res).forEach(r-> System.out.printf("水果id:%s,水果名称:%s%n",r.id(),r.name())); } if(f.isAnnotationPresent(RepeaDemo.class)){ RepeaDemo anno = f.getAnnotation(RepeaDemo.class); String id = anno.id(); String name = anno.name(); System.out.printf("水果id:%s,水果名称:%s%n",id,name); } }); } public static void main(String[] args) { demo2(); } }
执行结果如下:
水果id:1001,水果名称:荔枝
水果id:1003,水果名称:沃柑
水果id:1002,水果名称:葡萄
2.2 @Repeatable注解使用
对于上述注解使用,@Repeatable注解的作用就是使得@RepeaDemo注解可以多个添加在字段上,而最外层无须包裹@RepeaDemos注解,当然,@RepeaDemos注解任然需要保留,修改如下:
修改@RepeaDemos注解(注意@RepeaDemos注解,必须改为value()才可以):
package com.xiaoxu.tool.demo; import java.lang.annotation.*; @Target(value = {ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RepeaDemos { RepeaDemo[] value(); }
修改@RepeaDemo注解,增加@Repeatable,参数为RepeaDemos.class:
package com.xiaoxu.tool.demo; import java.lang.annotation.*; @Target(value = {ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(RepeaDemos.class) @Documented public @interface RepeaDemo { String id(); String name(); }
修改demo类:
package com.xiaoxu.tool.demo; import java.lang.reflect.Field; import java.util.Arrays; /** * @author xiaoxu * @date 2022-05-17 * spring_boot:com.xiaoxu.tool.demo.TestRepeatable */ public class TestRepeatable { // @RepeaDemos(value = { // @RepeaDemo(id = "1001",name = "荔枝"), // @RepeaDemo(id = "1003",name = "沃柑") // }) @RepeaDemo(id = "1001",name = "荔枝") @RepeaDemo(id = "1003",name = "沃柑") private String fruit1; @RepeaDemo(id = "1002",name = "葡萄") private String fruit2; public static void demo1(){ Class<?> a = TestRepeatable.class; Field[] fruits = a.getDeclaredFields(); Arrays.stream(fruits).forEach(f->{ if(f.isAnnotationPresent(RepeaDemo.class)){ RepeaDemo anno = f.getAnnotation(RepeaDemo.class); String id = anno.id(); String name = anno.name(); System.out.printf("水果id:%s,水果名称:%s%n",id,name); } }); } public static void demo2(){ Class<?> a = TestRepeatable.class; Field[] fruits = a.getDeclaredFields(); Arrays.stream(fruits).forEach(f->{ if(f.isAnnotationPresent(RepeaDemos.class)){ RepeaDemos anno = f.getAnnotation(RepeaDemos.class); RepeaDemo[] res= anno.value(); Arrays.stream(res).forEach(r-> System.out.printf("水果id:%s,水果名称:%s%n",r.id(),r.name())); } if(f.isAnnotationPresent(RepeaDemo.class)){ RepeaDemo anno = f.getAnnotation(RepeaDemo.class); String id = anno.id(); String name = anno.name(); System.out.printf("水果id:%s,水果名称:%s%n",id,name); } }); } public static void main(String[] args) { demo2(); } }
执行效果如下:
水果id:1001,水果名称:荔枝
水果id:1003,水果名称:沃柑
水果id:1002,水果名称:葡萄
可见,如下两种方式,获取的结果是一致的,其实@Repeatable注解就是一种语法糖(类似于python的装饰器语法糖等等),本质上在java编译时,使用如下的第2种注解方式,等同于使用第1种:
//1: @RepeaDemos(value = { @RepeaDemo(id = "1001",name = "荔枝"), @RepeaDemo(id = "1003",name = "沃柑") }) //2: @RepeaDemo(id = "1001",name = "荔枝") @RepeaDemo(id = "1003",name = "沃柑")
2.3 小结
1 @Repeatable注解使用前,需先增加一个注解的属性值为子注解数组,且名称为value()的父注解;
2 @Repeatable注解需要在子注解上标注,且@Repeatable注解的值为父注解class;
3 @Repeatable注解本质上是一种语法糖。
到此这篇关于Java的@Repeatable注解使用详细解析的文章就介绍到这了,更多相关@Repeatable注解的使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!