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.pswp.cn/bicheng/21082.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/21082.shtml
英文地址,請注明出處:http://en.pswp.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…