1. 前言
在日常开发中,如果我们希望某些组件加入到 Spring 容器的管理中,肌肉记忆使我们很自然的在某些类上加入了@Component 注解,但是往往不明所以,因此我在这里简单记录一下
2. @Component 相关注解
- @RestController
- @Controller
- @Service
- @Repository
- @Configuration
- 其它
这些注解的元注解都存在 @Component,在这里我们统称存在 @Component 注解
3. 源码解析
3.1 ConfigurationClassParser#doProcessConfigurationClass
doProcessConfigurationClass 这个方法比较重要,以下注解都可以处理:
- @ComponentScan
- @ComponentScans
- @Import
- @ImportResource
- @PropertySource
- @PropertySources
有兴趣的小伙伴,可以阅读相关博文:Spring之ConfigurationClassPostProcessor解析流程
3.2 ComponentScanAnnotationParser#parse
3.3 ClassPathBeanDefinitionScanner 构造方法
默认情况下,如果配置类上存在 @ComponentScan 注解,则会构建一个 ClassPathBeanDefinitionScanner 对象,并且给其 includeFilters 属性赋值,includeFilters 明细如下:
- new AnnotationTypeFilter(Component.class)
- new AnnotationTypeFilter(ManagedBean.class):如果相关类存在
- new AnnotationTypeFilter(Named.class):如果相关类存在
3.4 ClassPathBeanDefinitionScanner#doScan
由源码可以,如果 isCandidateComponent 方法返回 true,则会构建一个ScannedGenericBeanDefinition 对象,添加到候选者列表中。所以 metadataReader 对象只需要不满足 excludeFilters 的 match 方法,并且满足 includeFilters 的 match 方法,则表示这是一个候选者。
3.5 AnnotationTypeFilter#match
3.5.1 AnnotationTypeFilter 构造方法
根据上述源码, 相关属性值如下:
- considerInherited : false
- considerInterfaces : false
- annotationType : Component.class
- considerMetaAnnotations : true
主要调用父类(AbstractTypeHierarchyTraversingFilter)的 match 方法,流程图如下:
因为 considerInherited、considerInterfaces 值为 false,我们主要关注 AnnotationTypeFilter 的 matchSelf 方法
3.5.2 AnnotationTypeFilter#matchSelf
即对于 new AnnotationTypeFilter(Component.class) 这个 AnnotationTypeFilter,只要类上存在@Component 注解或者 @Component 注解的包装注解,都会返回true。所以默认情况下只要类上存在 @Component 注解都会被扫描、解析成一个 ScannedGenericBeanDefinition 对象,后期再根据 bean 的建模对象(BeanDefinition)实例化成 bean。@ManagedBean、@Named 注解相关的 AnnotationTypeFilter 也是同理
4. 案例演示
4.1 类上存在 @ManagedBean 注解,最终被扫描成 Bean
4.1.1 创建实体类 NamedComponent
@ManagedBean
public class NamedComponent {
}
4.1.2 创建配置类 AppConfig
@ComponentScan("com.test.config")
public class AppConfig {
}
4.1.3 创建启动类 Main
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
NamedComponent bean = context.getBean(NamedComponent.class);
System.out.println(bean);
}
}
4.1.4 执行 main 方法
通过运行结果,得出结论:Spring 容器中存在类型为 NamedComponent 的 bean,即类上存在 @ManagedBean 注解,最终被扫描成 Bean
4.2 @ComponentScan 将存在自定义注解的 class 扫描、解析、实例化成 bean
4.2.1 创建自定义注解 TestComponent
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestComponent {
}
4.2.2 创建实体类 MyComponent,类上标记自定义注解 @TestComponent
@TestComponent
public class MyComponent {
}
4.2.3 修改配置类 AppConfig
@ComponentScan(basePackages = "com.test.config",
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = TestComponent.class)})
public class AppConfig {
}
4.2.4 修改启动类
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyComponent myComponent = context.getBean(MyComponent.class);
System.out.println(myComponent);
NamedComponent bean = context.getBean(NamedComponent.class);
System.out.println(bean);
}
}
4.2.5 执行main方法
4.2.6 注意点
如果 @ComponentScan 注解的 useDefaultFilters 属性为 false,即使类上存在 @Component、@ManagedBean、@Named 注解,也不会被扫描、解析、实例化成 bean
@ComponentScan(basePackages = "com.test.config",
useDefaultFilters = false,
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = TestComponent.class)})
public class AppConfig {
}
5. 小结
- 默认情况下,如果类上存在 @Component、@ManagedBean、@Named 注解,则会被@ComponentScan 注解被扫描、解析、实例化成 bean。
- 如果 @ComponentScan 注解的 useDefaultFilters 属性为 false,即使类上存在 @Component、@ManagedBean、@Named 注解,也不会被扫描、解析、实例化成 bean
- @ComponentScan 注解还可以自定义 includeFilters,只要 includeFilters 或 defaultFilters 存在一个filter 的 match 方法返回 true,相关类就会被扫描、解析、实例化成 bean