您的位置:首页 > 游戏 > 游戏 > spring:解决findMergedRepeatableAnnotations获取可重复的元注解(meta-annotation)结果不正确问题

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

2024/11/16 5:56:29 来源:https://blog.csdn.net/10km/article/details/139394775  浏览:    关键词: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()

	/*** 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;}

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

看到这个方法注释,我就知道我应该找到了答案。

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

	@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)

总结

org.springframework.core.annotation.AnnotationUtilsorg.springframework.core.annotation.AnnotatedElementUtils中的静态方法只是注解工具常用场景的便利化封装,比如本例的AnnotatedElementUtils.findMergedRepeatableAnnotations方法,
对于获取有复层级关系的可重复的元注解(meta-annotation),它就并不一定适用,而应该直接使用更底层的org.springframework.core.annotation.MergedAnnotations中的工具方法来根据实际场景实现注解分析提取。

完整测试代码

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() {};}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com