spring-core:获取类/方法/字段/字段上直接定义的注解

AnnotatedElement.getAnnotation

如何获取一个类上定义的注解?
这个问题似乎不应该问,我们知道如果要获取一个类/方法/字段/字段上直接定义的注解是很方便的,如Class.getAnnotation(Class<CasbanScan>)就能实现,
只要实现了java.lang.reflect.AnnotatedElement接口的对象(Class,Method,Field)都有这个方法,
但是在Spring框架下,如果注解中有@AliasFor定义了别名的字段,要区别对待。
如下示例,
Class.getAnnotation获取注解对象,basePackages字段是空的,value字段才能获取正确的值。

	@Test
	public void test2GetDirectlyAnnotation() {
		try {
			{
				System.out.printf("-----> For %s\n",UserSummy.class.getSimpleName());
				System.out.println("\t==== USING Class.getAnnotation ====");
				CasbanScan annot = UserSummy.class.getAnnotation(CasbanScan.class);
				System.out.println("\t"+annot.toString());
				assertArrayEquals(new String[] {"hello"}, annot.value());
				System.out.printf("\tvalue() size:%d\n",annot.value().length);
				System.out.printf("\tbasePackages() size:%d\n",annot.basePackages().length);
				assertEquals(0, annot.basePackages().length);
			}
		} catch (Throwable e) {
			e.printStackTrace();
			fail();
		}
	}
	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.TYPE)
	public @interface CasbanScan {
		@AliasFor("basePackages")
		String[] value() default {};
		@AliasFor("value")
		String[] basePackages() default {};
	}

	@CasbanScan("hello")
	public static class UserSummy{
	}

Spring框架将注解(Annotation)用到了极致,对注解定义扩展了很多灵活了定义,@AliasFor就是一种灵活扩展。所以在Spring框架下,要获取正确直接定义在类上的注解。Class.getAnnotation(Class<CasbanScan>)方法并不一定合适。

Spring-core

SearchStrategy

为适应各种场景,Spring通过枚举类型SearchStrategy来定义搜索策略,
其中SearchStrategy.DIRECT 定义为仅查找直接声明的注释,而不考虑@Inherited注释,也不搜索父类或实现的接口。
所以只要使用SearchStrategy.DIRECT 作为搜索策略就只查找直接声明的注释。

MergedAnnotations

MergedAnnotations.from(AnnotatedElement element)方法就是使用SearchStrategy.DIRECT 作为搜索策略 创建一个新的MergedAnnotations实例,其中包含来自指定元素的所有注释和元注释
如下是MergedAnnotations.from(AnnotatedElement element)方法的实现

	static MergedAnnotations from(AnnotatedElement element) {
		return from(element, SearchStrategy.DIRECT);
	}

所以我们可以使用MergedAnnotations.from(AnnotatedElement element)方法来获取直接注解定义,

MergedAnnotation<CasbanScan> mergedAnnotation = 
	MergedAnnotations.from(UserSummy.class).get(CasbanScan.class);
if(mergedAnnotation.isDirectlyPresent()){
	System.out.println("FOUND @CasbanScan in UserSummy");
	/** 将 MergedAnnotation转为方便输出的 AnnotationAttributes 对象 */
	System.out.println(mergedAnnotation.asAnnotationAttributes());
	/** 直接从MergedAnnotation对象读取字段 */
	System.out.println(mergedAnnotation.getStringArray("basePackages")[0]);
	System.out.println(mergedAnnotation.getStringArray("value")[0]);
}

完整测试代码如下:

import static org.junit.Assert.*;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.Test;

import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;

public class SpringAnnotationUtilsTest {

	@Test
	public void test2GetDirectlyAnnotation() {
		try {
			System.out.printf("-----> For %s\n",UserSummy.class.getSimpleName());
			{
				/**
				 * 直接调用 Class.getAnnotation方法可以读取到注解,但对于用@AliasFor定义了别名的字段,要区别对待。
				 * 如下,basePackages字段是空的,value字段才能获取正确的值 
				 */
				System.out.println("\t==== USING Class.getAnnotation ====");
				CasbanScan annot = UserSummy.class.getAnnotation(CasbanScan.class);
				System.out.println("\t"+annot.toString());
				assertArrayEquals(new String[] {"hello"}, annot.value());
				System.out.printf("\tvalue() size:%d\n",annot.value().length);
				System.out.printf("\tbasePackages() size:%d\n",annot.basePackages().length);
				assertEquals(0, annot.basePackages().length);
			}
			{
				System.out.println("\t==== USING AnnotationUtils.isAnnotationDeclaredLocally  ====");
				boolean declaredLocally = AnnotationUtils.isAnnotationDeclaredLocally(CasbanScan.class, UserSummy.class);
				System.out.printf("\t@%s declaredLocally in %s:%b\n",CasbanScan.class.getSimpleName(),UserSummy.class.getSimpleName(), declaredLocally);
				assertTrue(declaredLocally);
				System.out.println("\t==== USING Spring ====");
				MergedAnnotations mergedAnnotations= MergedAnnotations.from(UserSummy.class);
				assertTrue(mergedAnnotations.isDirectlyPresent(CasbanScan.class));
				MergedAnnotation<CasbanScan> mergedAnnotation= mergedAnnotations.get(CasbanScan.class);
				assertTrue(mergedAnnotation.isDirectlyPresent());
				/** 将 MergedAnnotation转为方便输出的 AnnotationAttributes 对象 */
				System.out.println("\t"+mergedAnnotation.asAnnotationAttributes());
				/** 直接从MergedAnnotation对象读取字段 */
				assertArrayEquals(new String[] {"hello"}, mergedAnnotation.getStringArray("basePackages"));
				assertArrayEquals(new String[] {"hello"}, mergedAnnotation.getStringArray("value"));
				System.out.println("\t==== USING Annotation synthesized ====");
				/**
				 * 获取合成注解对象 
				  */
				CasbanScan annot = mergedAnnotation.synthesize();
				/** 调用AnnotationUtils.getAnnotationAttributes将 Annotation对象转为方便输出的Map  */
				System.out.printf("\t%s\n", AnnotationUtils.getAnnotationAttributes(annot));
				/*
				 * 对于用@AliasFor定义了别名的字段不论用读取哪个字段都是有正确的值
				 */
				assertArrayEquals(new String[] {"hello"}, annot.value());
				assertArrayEquals(new String[] {"hello"}, annot.basePackages());
			}
			{
				System.out.printf("-----> For %s\n",VipSummy.class.getSimpleName());
				MergedAnnotations mergedAnnotations= MergedAnnotations.from(VipSummy.class);
				boolean declaredLocally = mergedAnnotations.isDirectlyPresent(CasbanScan.class);
				System.out.printf("\t%s FOUND directly @%s in %s\n", declaredLocally?"":"NOT",CasbanScan.class.getSimpleName(),VipSummy.class.getSimpleName());
				assertFalse(mergedAnnotations.isDirectlyPresent(CasbanScan.class));
				MergedAnnotation<CasbanScan> mergedAnnotation= mergedAnnotations.get(CasbanScan.class);
				assertFalse(mergedAnnotation.isDirectlyPresent());
				
			}
		} catch (Throwable e) {
			e.printStackTrace();
			fail();
		}
	}
	
	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.TYPE)
	public @interface CasbanScan {
		@AliasFor("basePackages")
		String[] value() default {};
		@AliasFor("value")
		String[] basePackages() default {};
	}

	@CasbanScan("hello")
	public static class UserSummy{
	}
	public static class VipSummy extends UserSummy{
	}
}

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值