Java的@Repeatable注解使用详细解析

 更新时间:2024年02月02日 08:32:51   作者:小徐也要努力鸭  
这篇文章主要介绍了Java的@Repeatable注解使用详细解析,java8新增了注解@Repeatable,在hibernate-validator的源码注解如@MAX、@NotNull等中,有@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注解的使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java下载项目中静态文件方式

    Java下载项目中静态文件方式

    这篇文章主要介绍了Java下载项目中静态文件方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • 详解Java反射实现Aop代理

    详解Java反射实现Aop代理

    本篇文章主要介绍了Java 反射实现 Aop 代理,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • Java贪吃蛇游戏完善版

    Java贪吃蛇游戏完善版

    这篇文章主要为大家详细介绍了Java贪吃蛇游戏完善版,支持菜单操作,键盘监听,可加速,减速,统计得分等功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-04-04
  • Java使用Arrays.sort()方法实现给对象排序

    Java使用Arrays.sort()方法实现给对象排序

    这篇文章主要介绍了Java使用Arrays.sort()方法实现给对象排序,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 以Java代码为例讲解设计模式中的简单工厂模式

    以Java代码为例讲解设计模式中的简单工厂模式

    简单来说,工厂模式就是按照需求来返回一个类型的对象,使用工厂模式的意义就是,如果对象的实例化与代码依赖太大的话,不方便进行扩展和维护,使用工厂的目的就是使对象的实例化与主程序代码就行解耦.来具体看一下:
    2016-05-05
  • kafka-console-consumer.sh使用2次grep管道无法提取消息的解决

    kafka-console-consumer.sh使用2次grep管道无法提取消息的解决

    这篇文章主要介绍了kafka-console-consumer.sh使用2次grep管道无法提取消息的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • IntelliJ IDEA本地代码提交到github网站不显示与本地不同步问题的解决办法

    IntelliJ IDEA本地代码提交到github网站不显示与本地不同步问题的解决办法

    今天小编就为大家分享一篇关于IntelliJ IDEA本地代码提交到github网站不显示与本地不同步问题的解决办法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-10-10
  • java 全角半角字符转换如何实现

    java 全角半角字符转换如何实现

    在java中可能会用到过全角半角字符转换问题,于是网上搜索整理了一下,晒出来和大家分享,希望可以帮助你们
    2012-12-12
  • java 分割csv数据的实例详解

    java 分割csv数据的实例详解

    这篇文章主要介绍了java 分割csv数据的实例详解的相关资料,这里提供了简单实例,需要的朋友可以参考下
    2017-07-07
  • 浅谈java中字节与字符的区别

    浅谈java中字节与字符的区别

    这篇文章主要介绍了浅谈java中字节与字符的区别,字节是java中的基本数据类型,用来申明字节型的变量;字符是语义上的单位,它是有编码的,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07

最新评论