持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
基于 Spring Framework v5.2.6.RELEASE
概述
之前用三篇文章,以 AnnotationConfigApplicationContext 为切入点,分析了 Spring 基于注解组件扫描的上下文初始化原理,其中介绍到了 Spring 会在指定的包路径下,查找所有添加了@Component注解的类型,基于这些类型创建对应的 ScannedGenericBeanDefinition,然后注册到容器里。
本文来从源码中,了解一下@Component注解。
@Component注解
我们先来看一下@Component注解的源码。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
String value() default "";
}
@Component注解包含了4个元注解,下面分别来解释一下:
@Target(ElementType.TYPE)代表这个注解可以用在类、接口、枚举上,包括其他注解。@Retention(RetentionPolicy.RUNTIME)代表注解在运行时生效。@Documented可以使 javadoc 或者类似的工具输出文档中包含这个注解。@Indexed用于提升 Spring 组件扫描的性能。
在注解中,有一个value属性,当一个类被@Component注解修饰时,Spring 会扫描到并生成一个对应的 BeanDefinition,也就是说,每一个被@Component注解修饰的类,都对应一个 Spring Bean,这里的value属性,就是用来指定 Bean 的名称的,这里指定的名称会代替 Spring 自动生成的 Bean 名称。如果需要给一个组件指定名称,则给value属性赋值即可。
其他相关的注解
使用过 Spring 框架的小伙伴都知道,在日常的项目开发中,我们不止会用到@Component注解,经常使用的还包括@``Controller、@Service、@``Repository,这写类也会被 Spring 扫描组件时被筛选到,这是如何扫描到的呢?
上面提到的几个注解,都在spring-context工程的org.springframework.stereotype包下。
以@``Controller注解为例,它的源码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(annotation = Component.class)
String value() default "";
}
它包含了一个元注解@Component,并且指定了value属性与@Component注解的value属性的关系。@Service和@``Repository的定义,两个注解除了名称以外,其余都与@``Controller相同。
了解了他们之间的关系,我们接着看 Spring 是如何从指定包路径下所有的类型中筛选中符合条件的类型的。
通过 TypeFilter 筛选类
回顾之前对 AnnotationConfigApplicationContext 初始化过程的分析。在初始化上下文中的 ClassPathBeanDefinitionScanner 过程中,通过registerDefaultFilters设定了类型过滤器,其中默认添加了筛选@Component注解类型的 AnnotationTypeFilter。
其后,在进行组件扫描的过程中,对所有的类进行筛选时,会用到之前注册的 TypeFilter 的match方法来进行匹配。其中,AnnotationTypeFilter 的matchSelf方法会被调用,其源码如下。
@Override
protected boolean matchSelf(MetadataReader metadataReader) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
return metadata.hasAnnotation(this.annotationType.getName()) ||
(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}
也就是说,标记了@Component注解的类型,以及标记的注解被标记了@Component注解的类型都会筛选到,因此,被标记了@``Controller、@Service、@``Repository这几种注解的类也会被筛选到。这三个注解的区别,也更多地是语义上的区别。
更多
除了上面提到的三个注解以外,@``Configuration注解也是经常在基于 Spring 开发的工程中用到的注解,它的定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
boolean proxyBeanMethods() default true;
}
它也被标记了@Component元注解,但是,它的作用并不只是像@``Controller之类的注解用来给容器中提供一个 Bean 的定义那么简单,而是可以用来进行 Spring 应用的配置,比如通过@``Bean修饰的方法来注册一个 Bean,或者使用更多的注解进行更多的配置。可以说,一个@``Configuration注解修饰的类很类似一个单独的 Spring 配置文件。那么,Spring 是什么时候执行这些配置的呢,后面的文章会对这部分进行深入解析。