spring对注解的拓展,早已超越J2EE的规约。通过注解可以实现其派生性(Annotation Hierarchy)。自从spring2.5开始,每个大版本都对其进行了增强。
何为派生?
没错一般能想到的就是属性派生,直接上代码,其实Parent
和Child
看似是派生的关系,但实则并无关系!
它们是两个毫无关联的接口(我在面试的时候会提问:注解和接口的关系),通过javap -v .\Child.class
便可得知
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Parent {
String name() default "a";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Parent
public @interface Child {
String name() default "b";
}
@Child
class Home { }
@Test
public void metaTest() {
Annotation[] annotations = Home.class.getDeclaredAnnotations();
System.out.println(Arrays.toString(annotations));//[@com.yh.lucky.day.service.Child(name=Jack)]
annotations = annotations[0].annotationType().getDeclaredAnnotations();
System.out.println(Arrays.toString(annotations));//[@java.lang.annotation.Target(value=[TYPE]), @java.lang.annotation.Retention(value=RUNTIME), @java.lang.annotation.Documented(), @com.yh.lucky.day.service.Parent(name=John)]
}
那派生就不是真正意义上的派生(Override),而spring对其加以拓展,将其界定在层次性:
@Parent @Child
也就是说,有层次结构的注解,spring就认定是具备派生性(extends),并且派生性强调的类型(Child extends Parent)。
Component
在spring中有很多*@Repository,@Service,@Controller,@Configuration等*均派生于@Component
,这也就是为什么你添加了一个注解(只需要标记为@Component
),而不用改动spring core。
- Spring2.5 仅支持单层次的@Component派生,未采用层次递归获取Annotation[]
- Spring3.x 实现仅两层@Component派生
- Spring4.x开始采用递归方式查找元注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface HignLevelAnnotation {
String value() default "a";
}
@HignLevelAnnotation
public class TestComponent {//此时可以自动注入到spring ioc中
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@HignLevelAnnotation
public @interface MiddleLevelAnnotation {
String value() default "b";
}
@MiddleLevelAnnotation
public class TestComponent {//此时还可以自动注入到spring ioc中
}
@Test
public void metaTest() throws IOException {
CachingMetadataReaderFactory readerFactory = new CachingMetadataReaderFactory();
//ResourcePatternUtils
Resource resource = new ClassPathResource("TestComponent.class",TestComponent.class);
AnnotationMetadata asmVisitor = readerFactory.getMetadataReader(resource).getAnnotationMetadata();
//AnnotationMetadataReadingVisitor#attributesMap内部存储了注解记录
System.out.println(((AnnotationMetadataReadingVisitor)asmVisitor).isAnnotated("org.springframework.stereotype.Component"));
//上面的Asm可能难懂,那下面反射的更易懂
Component cp = AnnotationUtils.findAnnotation(TestComponent.class, Component.class);
System.out.println(cp.value());
HignLevelAnnotation cp2 = AnnotationUtils.findAnnotation(TestComponent.class, HignLevelAnnotation.class);
System.out.println(cp2.value());
}
原理-AnnotationMetadataReadingVisitor
在之前的章节讲过MetadataReaderFactory,读取类或者注解相关信息有两种方式:反射和ASM。
-
ClassMetadata
,类元信息抽象,StandardClassMetadata
(反射),ClassReader
(ASM) -
MethodMetadata
,方法元信息抽象,StandardMethodMetadata
(反射),MethodMetadataReadingVisitor
(ASM) -
AnnotationMetadata
,注解元信息抽象,StandardAnnotationMetadata
(反射),AnnotationMetadataReadingVisitor
(ASM) -
AnnotationAttributes
,注解属性抽象 -
MetadataReader
,元信息读取抽象,通过MetadataReaderFactory
获取
而``AnnotationMetadataReadingVisitor的内部实现使用
AnnotationAttributesReadingVisitor`类来递归的查找元注解。反射的API类`AnnotationUtils`易用性则更强。
那要满足真正意义上的派生,不仅仅时类型,spring为此开发了@AliasFor
。