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{
}
}