理解Spring注解编程模型是深入探讨Spring Boot注解驱动及自动装配的重要前提。
Spring Annotation Programming Model
Spring Annotation API
Spring使用AnnotationMetadata
接口定义对某个类的注解的抽象访问。它有两种实现,一种是基于Java反射API的StandardAnnotationMetadata
;一种是使用ASM的SimpleAnnotationMetadata
。基于Java反射API的实现有一个前提,被反射的Class必须被ClassLoader加载,因此ASM的实现适用于不应该加载Class又需要获取Class内注解信息的情况。ASM的实现方式在性能上要优于Java反射API的实现方式。
罗列一下之后会用到的一些方法:
1 | /** |
在Java反射编程模型中,注解之间无法继承,也不能实现接口,不过Java语言默认将所有注解实现Annotation接口,被标注的对象用API AnnotatedElement表达。通过**AnnotatedElement#getAnnotation(Class)**方法返回指定类型的注解对象,获取注解属性需要显式地调用对应的属性方法。
1 | // name 属性内容 |
名词解释
元注解(Meta-Annotations)
A meta-annotation is an annotation that is declared on another annotation. An annotation is therefore meta-annotated if it is annotated with another annotation. For example, any annotation that is declared to be documented is meta-annotated with
@Documented
from thejava.lang.annotation
package.
所谓元注解是指一个能声明在其他注解上的注解。在Spring的使用场景中,@Component
就是标准的元注解。
Spring模式注解(Stereotype Annotations)
A stereotype annotation is an annotation that is used to declare the role that a component plays within the application.
@Component
is a generic stereotype for any Spring-managed component. Any component annotated with@Component
is a candidate for component scanning. Similarly, any component annotated with an annotation that is itself meta-annotated with@Component
is also a candidate for component scanning.Core Spring provides several stereotype annotations out of the box, including but not limited to:
@Component
,@Service
,@Repository
,@Controller
,@RestController
, and@Configuration
.@Repository
,@Service
由于Java语言规范规定,Annotation不允许继承,没有类派生子类的能力。因此Spring采用元标注的方式实现注解之间的“派生”。
1 |
|
Spring @Component注解派生性原理
Spring的组件扫描是由ClassPathBeanDefinitionScanner和AnnotationTypeFilter相互配合实现的。ClassPathBeanDefinitionScanner默认在指定根包路径(basePackage)下,将查找所有标注@Component及“派生”注解的BeanDefinition集合,并且默认的过滤规则由AnnotationTypeFilter以及@Component的元信息(ClassMetaData和AnnotationMetaData)共同决定。
默认的includeFilters:
1 | this.includeFilters.add(new AnnotationTypeFilter(Component.class)); |
AnnotationTypeFilter会匹配给定的注解类型(@Component)以及以其作为元注解的注解类型,默认的是@Repository、@Service、@Controller。官方文档只写了这4种,但是Spring Framework3.0以后引入的@Configuration也是一样的。
但是AnnotationTypeFilter只能匹配直接标注@Component作为元注解的注解类型,无法匹配多层级的注解类型(例如:@SpringBootConfiguration)
ClassPathBeanDefinitionScanner#doScan方法使用MetadataReader接口的具体实现SimpleMetadataReader构造SimpleAnnotationMetadataReadingVisitor
Spring Framework从4.x开始,通过AnnotationAttributessReadingVisitor采用递归的方式查找元注解,从而支持多层次派生。
Spring 组合注解(Composed Annotations)
A composed annotation is an annotation that is meta-annotated with one or more annotations with the intent of combining the behavior associated with those meta-annotations into a single custom annotation. For example, an annotation named
@TransactionalService
that is meta-annotated with Spring’s@Transactional
and@Service
annotations is a composed annotation that combines the semantics of@Transactional
and@Service
.@TransactionalService
is technically also a custom stereotype annotation.
Spring组合注解和模式注解的元信息抽象是一样的,均由AnnotationMetadata API抽象表达,具体某个注解的“元注解”信息则通过getMetaAnnotationTypes(String)方法查询。
Spring注解属性别名和覆盖(Attribute Aliases and Overrides)
Spring注解元信息抽象AnnotationMetadata
提供了getMetaAnnotationTypes(String)方法获取所有元注解类型集合,再结合getAnnotationAttributes(String)方法返回注解所有关联的属性信息,以Map结构储存。AnnotationMetadata
有两种实现:基于ASM的AnnotationMetadataReadingVisitor和Java反射API的StandardAnnotationMetadata。
Spring提供两种实现的可能是考虑到不同的使用场景,凡是基于Java反射API的实现必然需要一个大前提,即被反射的Class必须被ClassLoader加载。当Spring应用指定的Java package扫描Spring模式注解时,应用并不需要、更不应该把package下所有的Class悉数加载,所以需要用到ASM的方式直接读取Class文件信息。此外ASM实现方式在性能上要优于反射的实现方式。
AnnotationAttributes是Spring对注解属性的抽象,它直接拓展了LinkedHashMap,既使用Key-Value的数据结构,又确保了其顺序与属性方法声明一致。同时由于它采用Map结构,若不同层次的元注解之间出现同名属性,则必然出现重叠的情况。
An attribute override is an annotation attribute that overrides (or shadows) an annotation attribute in a meta-annotation. Attribute overrides can be categorized as follows.
- Implicit Overrides: given attribute
A
in annotation@One
and attributeA
in annotation@Two
, if@One
is meta-annotated with@Two
, then attributeA
in annotation@One
is an implicit override for attributeA
in annotation@Two
based solely on a naming convention (i.e., both attributes are namedA
).- Explicit Overrides: if attribute
A
is declared as an alias for attributeB
in a meta-annotation via@AliasFor
, thenA
is an explicit override forB
.- Transitive Explicit Overrides: if attribute
A
in annotation@One
is an explicit override for attributeB
in annotation@Two
andB
is an explicit override for attributeC
in annotation@Three
, thenA
is a transitive explicit override forC
following the law of transitivity.
覆盖采取就近原则,例如@Service
的value属性会覆盖@Component
的value属性。或者使用@AliasFor
进行覆盖。