相关文章:Spring:获取容器中的Bean
相关文章:Spring Boot:容器加载时执行特定操作
前言
- 在我们刚开始接触Spring的时,要定义bean的话需要在xml中编写,比如
<bean id="myBean" class="your.pkg.YourClass"/>
- 但当 bean 多的时候则非常麻烦,于是出了一个 component-scan 来指定扫描的包,它会去扫描这个包下的所有 class
<context:component-scan base-package="your.pkg"/>
- 后来注解流行起来,出现了 @ComponentScan 注解,作用跟 component-scan 一样
@ComponentScan(basePackages = {"your.pkg", "other.pkg"})
public class Application { ... }
- 但无论是哪一种方式,它们只能扫描 Spring 定义的注解,例如 @Component、@Service 等,若要扫描自定义注解,就要自定义扫描器
Spring 内置的扫描器
- component-scan 标签底层使用 ClassPathBeanDefinitionScanner 该类来完成扫描工作。
- @ComponentScan 注解配合 @Configuration 注解使用,底层使用 ComponentScanAnnotationParser 解析器完成解析工作
- ComponentScanAnnotationParser 解析器内部使用 ClassPathBeanDefinitionScanner 扫描器,其内部处理过程如下:
- 遍历 basePackages 找出包下的所有的 class,封装成 Resource 接口集合,这个 Resource 接口是 Spring 对资源的封装,有 FileSystemResource、ClassPathResource、UrlResource 实现等
- 遍历 Resource 集合,通过 includeFilters 和 excludeFilters 判断是否解析。这里的 includeFilters 和 excludeFilters 是 TypeFilter 接口类型的集合。TypeFilter 接口是一个用于判断类型是否满足要求的类型过滤器
- excludeFilters 中只要有一个 TypeFilter 满足条件,这个 Resource 就会被过滤。includeFilters 中只要有一个 TypeFilter 满足条件,这个 Resource 就不会被过滤
- 把所匹配的 Resource 封装成 ScannedGenericBeanDefinition 添加到 BeanDefinition 结果集中并返回
- ClassPathBeanDefinitionScanner 继承 ClassPathScanningCandidateComponentProvider
,它的构造函数提供了一个 useDefaultFilters 参数,若为 true 则会添加默认的 TypeFilter
public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) {
this(useDefaultFilters, new StandardEnvironment());
}
TypeFilter 接口
public interface TypeFilter {
boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException;
}
- TypeFilter 接口有一些实现类,例如:
- AnnotationTypeFilter :类是否有注解修饰
- RegexPatternTypeFilter:类名是否满足正则表达式
自定义首描
- 通常情况下,要完成扫包功能,可以直接使用 ClassPathScanningCandidateComponentProvider 完成,并加上 TypeFilter 即可,例如扫描某个包下带有 @Test 注解的类
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); // 不使用默认的TypeFilter
provider.addIncludeFilter(new AnnotationTypeFilter(Test.class));
Set<BeanDefinition> beanDefinitionSet = provider.findCandidateComponents("spring.demo.entity");
- 若需要更复杂的功能,可继承 ClassPathScanningCandidateComponentProvider 实现自定义扫描器
网友评论