spring:解决findMergedRepeatableAnnotations获取可重复的元注解(meta-annotation)结果不正确问题

spring-core的注解工具提供的方法 AnnotatedElementUtils.findMergedRepeatableAnnotations用于从AnnotatedElement 对象获取可重复的注解。但如果注解本身也是可以定义在其他注解之上的元注解(meta-annotation),且该注解也是可重复注解。这个方法就可能会失效。这就是我最近在使用spring注解工具时遇到的问题。示例如下。

注解定义

先定义一组注解:

	@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })@Repeatable(LevelAs.class)public @interface LevelA {String value();}@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.METHOD })public @interface LevelAs {LevelA[] value();}@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.ANNOTATION_TYPE,ElementType.METHOD })@LevelA("shadows")@Repeatable(LevelBs.class)public @interface LevelB {@AliasFor(annotation = LevelA.class,attribute = "value")String value();}@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.METHOD })public @interface LevelBs {LevelB[] value();}@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.METHOD })@LevelB("shadows")public @interface LevelC {@AliasFor(annotation = LevelB.class,attribute = "value")String value();}

上面定义了@LevalA,@LevalB,@LevalC三个注解,其中@LeveA,@LevelB是可以重复注解(参见 @LevelAs,@LevelBs),而且还是元注解。@LeveA@LevelB的元注解,@LevelB@LevelC的元注解。它们三个层级关系是这样的。

@LevelA				可重复 @LevelAs└─@LevelB		可重复 @LevelBs└─@LevelC

如下定义了测试这些注解的类

	public static class TestClass{@LevelB("hello")@LevelB("world")@LevelA("top1")@LevelA("top2")@LevelC("jerry")public void foo() {};@LevelB("hello")@LevelA("top1")@LevelA("top2")@LevelC("jerry")public void tar() {};}

findMergedRepeatableAnnotations

如下调用AnnotatedElementUtils.findMergedRepeatableAnnotations方法读取TestClass.tar上定义的所有@LevelA注解

	@Testpublic void test1() {try {Method method = TestClass.class.getMethod("tar");Set<LevelA> set = AnnotatedElementUtils.findMergedRepeatableAnnotations(method,LevelA.class);set.stream().forEach(a->{System.out.printf("%s\n", a);});} catch (Throwable e) {e.printStackTrace();fail();}}

确实可以获取正确的结果:

@RepeatableAnnotTest$LevelA(value=top1)
@RepeatableAnnotTest$LevelA(value=top2)
@RepeatableAnnotTest$LevelA(value=hello)
@RepeatableAnnotTest$LevelA(value=jerry)

那是因为TestClass.tar上只定义了一个@LevelB注解,如果上面的测试代码只是把方法名换为定义了多个@LevelB的方法foo,结果就是这样的:

@RepeatableAnnotTest$LevelA(value=top1)
@RepeatableAnnotTest$LevelA(value=top2)
@RepeatableAnnotTest$LevelA(value=jerry)

因为这里foo方法定义了超过一个@LevelB注解,实际定义在foo上的注解就成了@LeveBs({@LevelB("hello"),@LevelB("world")}),就导致AnnotatedElementUtils.findMergedRepeatableAnnotations方法不能正确获取。
分析AnnotatedElementUtils的源码,找到findMergedRepeatableAnnotations方法实际的核心执行代码如下:

RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType);
return MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, repeatableContainers);

RepeatableContainers

进一查看RepeatableContainers的代码发现它还有另一个方法standardRepeatables()

创建一个使用Java的@Repeatable注释进行搜索的RepeatableContainers实例。

	/*** Create a {@link RepeatableContainers} instance that searches using Java's* {@link Repeatable @Repeatable} annotation.* @return a {@link RepeatableContainers} instance*/public static RepeatableContainers standardRepeatables() {return StandardRepeatableContainers.INSTANCE;}

于是照着上面的代码将测试代码修改如下:

	@Testpublic void test3() {try {Method method = TestClass.class.getMethod("foo");RepeatableContainers repeatableContainers = RepeatableContainers.standardRepeatables();MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY, repeatableContainers).stream(LevelA.class).forEach(a->{System.out.printf("%s\n", a.synthesize());});} catch (Throwable e) {e.printStackTrace();fail();}}

则结果正确:

@LevelA(value=top1)
@LevelA(value=top2)
@LevelA(value=hello)
@LevelA(value=world)
@LevelA(value=jerry)

完整测试代码

import static org.junit.Assert.*;import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.Set;
import org.junit.Test;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.core.annotation.RepeatableContainers;public class RepeatableAnnotTest {@Testpublic void test1() {try {Method method = TestClass.class.getMethod("foo");Set<LevelA> set = AnnotatedElementUtils.findMergedRepeatableAnnotations(method,LevelA.class);set.stream().forEach(a->{System.out.printf("%s\n", a);});} catch (Throwable e) {e.printStackTrace();fail();}}@Testpublic void test2() {try {Method method = TestClass.class.getMethod("foo");RepeatableContainers repeatableContainers = RepeatableContainers.of(LevelA.class,null);MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY,repeatableContainers) .stream(LevelA.class) /* .stream() .sorted((o1,o2)->Integer.compare(o1.getDistance(), o2.getDistance()))*/.forEach(a->{System.out.printf("%s\n", a.synthesize());});} catch (Throwable e) {e.printStackTrace();fail();}}@Testpublic void test3() {try {Method method = TestClass.class.getMethod("foo");RepeatableContainers repeatableContainers = RepeatableContainers.standardRepeatables();MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY, repeatableContainers).stream(LevelA.class).forEach(a->{System.out.printf("%s\n", a.synthesize());});} catch (Throwable e) {e.printStackTrace();fail();}}@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })@Repeatable(LevelAs.class)public @interface LevelA {String value();}@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.METHOD })public @interface LevelAs {LevelA[] value();}@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.ANNOTATION_TYPE,ElementType.METHOD })@LevelA("shadows")@Repeatable(LevelBs.class)public @interface LevelB {@AliasFor(annotation = LevelA.class,attribute = "value")String value();}@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.METHOD })public @interface LevelBs {LevelB[] value();}@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.METHOD })@LevelB("shadows")public @interface LevelC {@AliasFor(annotation = LevelB.class,attribute = "value")String value();}public static class TestClass{@LevelB("hello")@LevelB("world")@LevelA("top1")@LevelA("top2")@LevelC("jerry")public void foo() {};@LevelB("hello")@LevelA("top1")@LevelA("top2")@LevelC("jerry")public void tar() {};}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/21082.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于java18多端展示+ idea hbuilder+ mysql家政预约上门服务系统,源码交付,支持二次开发

基于java18多端展示 idea hbuilder mysql家政预约上门服务系统&#xff0c;源码交付&#xff0c;支持二次开发 家政预约上门系统是一种通过互联网或移动应用平台&#xff0c;为用户提供在线预约、下单、支付和评价家政服务的系统。该系统整合了家政服务资源&#xff0c;使用户能…

RabbitMQ三、springboot整合rabbitmq(消息可靠性、高级特性)

一、springboot整合RabbitMQ&#xff08;jdk17&#xff09;&#xff08;创建两个项目&#xff0c;一个生产者项目&#xff0c;一个消费者项目&#xff09; 上面使用原生JAVA操作RabbitMQ较为繁琐&#xff0c;很多的代码都是重复书写的&#xff0c;使用springboot可以简化代码的…

Vue3集成Phaser-飞机大战游戏(设计与源码)

文章目录 引言项目初始化游戏设计和结构游戏程序实现Vue页面嵌入PhaserPreloader 场景加载游戏场景功能实现功能类定义Boom爆炸类Bullet子弹类Enemy敌军类Player玩家类End游戏结束类 总结 更多相关内容可查看 引言 飞机大战&#xff08;也被称为射击游戏或空战游戏&#xff09…

轻松上手MYSQL:优化MySQL慢查询,让数据库起飞

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》《MYSQL应用》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 ✨欢迎加入探索MYSQL慢查询之旅✨ &#x1f44b; 大家好&#xff01;我是你们的…

如何优雅简洁的使用YOLOv8

如何优雅简洁的使用YOLOv8 目录训练调用代码如何一键训练多个yamlexport模型测试多个yaml是否运行正常predict本文提供了 如何优雅简洁的使用YOLOv8 🗝️YOLOv8实战宝典--星级指南:从入门到精通,您不可错过的技巧   -- 聚焦于YOLO的 最新版本, 对颈部网络改进、添加局…

Crosslink-NX器件应用连载(11): 图像(数据)远程传输

作者&#xff1a;Hello&#xff0c;Panda 大家下午好&#xff0c;晚上好。这里分享一个Lattice Crosslink-NX器件实现图像或数据&#xff08;卫星数据、雷达数据、ToF传感器数据等&#xff09;远程传输的案例&#xff08;因为所描述的内容颇杂&#xff0c;晒图不好晒&#xff…

react自用小技巧(持续更新中)

react自用小技巧&#xff08;持续更新中&#xff09; 作者&#xff1a;devwolf 导言&#xff1a; 笔者应届时&#xff0c;投vue2就任一家大食品厂的资讯部后转成了react&#xff0c;写了一年出头的react类组件。然后跳槽到苏州科技城的一个原做影视渲染的公司开始全面转hook…

文件批量改后缀名,轻松实现TXT到DOCX格式转换,高效管理您的文件库!

文件处理与管理已成为我们日常生活和工作中不可或缺的一环。然而&#xff0c;面对海量的文件&#xff0c;如何高效地进行格式转换和管理&#xff0c;却成为了一道难题。今天&#xff0c;我们将为您揭晓一个神奇的解决方案——文件批量改后缀名功能&#xff0c;让您轻松实现TXT到…

【GPT-4o:开创人工智能新纪元】

GPT-4o&#xff1a;开创人工智能新纪元 最近&#xff0c;GPT-4o问世&#xff0c;再次引发了人们对人工智能技术的关注和讨论。这一新型语言模型的出现&#xff0c;不仅在技术上实现了飞跃&#xff0c;也为我们带来了全新的思考和体验。接下来&#xff0c;我们将对GPT-4o进行全…

【docker】docker的安装

如果之前安装了旧版本的docker我们需要进行卸载&#xff1a; 卸载之前的旧版本 卸载 # 卸载旧版本 sudo apt-get remove docker docker-engine docker.io containerd runc # 卸载历史版本 apt-get purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker…

linux文件共享之samba

1.介绍 Samba是一个开源文件共享服务&#xff0c;可以使linux与windows之间进行文件共享&#xff0c;可以根据不同人员调整共享设置以及权限管理。 2.安装 一个命令就OK了&#xff1a;yum install -y samba [rootansible01 ~]# yum install -y samba 已加载插件&#xff1a;l…

Python爬虫之简单学习BeautifulSoup库,学习获取的对象常用方法,实战豆瓣Top250

BeautifulSoup是一个非常流行的Python库&#xff0c;广泛应用于网络爬虫开发中&#xff0c;用于解析HTML和XML文档&#xff0c;以便于从中提取所需数据。它是进行网页内容抓取和数据挖掘的强大工具。 功能特性 易于使用: 提供简洁的API&#xff0c;使得即使是对网页结构不熟悉…

【一刷《剑指Offer》】面试题 31:连续子数组的最大和

牛客对应题目链接&#xff1a;连续子数组最大和_牛客题霸_牛客网 (nowcoder.com) 力扣对应题目链接&#xff1a;53. 最大子数组和 - 力扣&#xff08;LeetCode&#xff09; 核心考点 &#xff1a;简单动归问题。 一、《剑指Offer》对应内容 二、分析题目 1、贪心 从前往后迭…

backbone主干网络的选取

backbone_name 通常用于指定深度学习模型的主干网络&#xff08;backbone network&#xff09;。主干网络是指在整个模型中承担主要特征提取任务的部分。不同的主干网络有不同的架构和特征提取能力&#xff0c;适用于不同的任务和数据集。 针对戴着口罩和戴着3D眼睛提取人脸特征…

关于Posix标准接口和Nuttx操作系统

基本介绍 主要参考&#xff1a; Linux 系统中的 POSIX 接口详细介绍_linux posix-CSDN博客 POSIX&#xff08;Portable Operating System Interface&#xff0c;可移植操作系统接口&#xff09;是由 IEEE&#xff08;Institute of Electrical and Electronics Engineers&#x…

大模型对齐方法笔记四:针对领域问答来进行知识对齐方法KnowPAT

KnowPAT KnowPAT(Knowledgeable Preference AlignmenT) 出自2023年11月的论文《Knowledgeable Preference Alignment for LLMs in Domain-specific Question Answering》&#xff0c;主要针对领域问答来进行知识对齐。 在领域问答有两个挑战&#xff1a;希望输出满足用户的要…

Notepad++ 常用

File Edit search view Encoding Language Settings Tools Macro Run Plugins Window 文件 编辑 搜索 视图 编码 语言 设置 工具 宏 运行 插件 窗口 快捷方式 定位行 &#xff1a;CTRL g查找&#xff1a; CTRL F替换&am…

小白也能看得懂的基于HTML+CSS+JS实现的五子棋小游戏

五子棋是一种起源于中国的传统棋类游戏&#xff0c;具有悠久的历史。 基本规则 棋盘&#xff1a; 五子棋通常在一个 15x15 的棋盘上进行&#xff0c;但也可以在更大的棋盘上进行。棋盘上的每个交叉点称为一个“点”。 棋子&#xff1a; 五子棋使用黑白两色的棋子。两名玩家分别…

【竞技宝】欧冠:多特抢开局失败,皇马展示顶级防守反击

本赛季欧冠决赛结束,皇马在上半场被压制的情况下,2比0击败多特蒙德夺得队史第15座欧冠冠军奖杯。比赛中多特蒙德已经展现出了不俗的状态,可是面对老辣的皇马他们还是败下阵来,皇马用顶级的防守反击给多特上了一课。通过这场比赛,相信球迷们也清楚当今足坛硬实力不可或缺。 在许…

《Effective C++》《资源管理——14、在资源管理类中小心copying行为》

文章目录 1、Terms14:Think carefully about copying behavior in resource-managing classes方法一&#xff1a;禁止复制方法二&#xff1a;对底层资源使出“引用计数法”方法三&#xff1a;复制底部资源方法四&#xff1a;转移底部资源的拥有权 2、总结3、参考 1、Terms14:Th…