Spring的组件扫描的核心组件是 ClassPathBeanDefinitionScanner ,即类路径下BeanDefinition扫描器
使用 ClassPathBeanDefinitionScanner 的3个要素
① BeanDefinitionRegistry 指明解析后产生的 BeanDefinition 存在哪
② basePackage 指明扫描路径
③ 定义扫描规则(通过构造方法指定),按哪种规则去扫描 (如默认的扫描规则是:待扫描的类上要标注 @Component、 @Repository、 @Service、@Controller 注解)
ClassPathBeanDefinitionScanner 的简单使用
// 创建 registry 对象
BeanDefinitionRegistry registry = new DefaultListableBeanFactory();
// 为 scanner 指定 registry
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
// 扫描指定包下的 bean
scanner.scan("com.springdemo.bean");
// 测试:获取bean
BeanFactory beanFactory = (BeanFactory) registry;
Object bean = beanFactory.getBean("xxx");
在使用 ClassPathBeanDefinitionScanner 时:
首先需要为其设置 BeanDefinitionRegistry,通常通过构造函数进行设置。(Spring容器实现类基本上都可以作为 registry 设置到 ClassPathBeanDefinitionScanner 中)
定义扫描规则:通过 boolean useDefaultFilters 来指定是否使用默认的过滤策略(默认过滤规则:只扫描标有 @Component 和其复合注解的组件)。这里例子中没有指定,就使用默认的
指定扫描路径:通过 ClassPathBeanDefinitionScanner#scan() 就可以扫描指定包下(“com.springdemo.bean”)的所有类文件,将符合条件的类作为 BeanDefinition,注册到 registry 中。
源码分析:
ClassPathBeanDefinitionScanner 对象的初始化
// 若不指定扫描规则,则使用默认的过滤策略,useDefaultFilters = true
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
this(registry, true);
}
// 可以自定义指定过滤策略 useDefaultFilters
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
}
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) {
this(registry, useDefaultFilters, environment, (registry instanceof ResourceLoader ? (ResourceLoader) registry : null));
}
所有构造方法,最终都会适配给最后一个构造:
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;
// 若使用默认的过滤规则(扫描规则),则注册默认的 TypeFilter
if (useDefaultFilters) {
registerDefaultFilters();
}
setEnvironment(environment);
setResourceLoader(resourceLoader);
}
通过 ClassPathBeanDefinitionScanner 的构造方法,可以指定:
1、BeanDefinitionRegistry 指明解析后产生的 BeanDefinition 存在哪
2、 定义扫描规则,按哪种规则去扫描
扫描规则是由 TypeFilter 组件决定的(下文会详细解析)。 默认的扫描规则,registerDefaultFilters() 方法会注册一些默认的 TypeFilter
scan()方法:组件扫描
先明白 ClassPathBeanDefinitionScanner 做了什么工作(具体的解析过程可以先不看)
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
// 组件扫描
doScan(basePackages);
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
重点关注 doScan() 方法:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 必须指定包路径: basePackages
Assert.notEmpty(basePackages, "At least one base package must be specified");
// Set集合用来存放后续解析出来的 BeanDefinition
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 【关键方法】:查找包下,所有符合条件的候选组件,将它们解析封装成 BeanDefinition
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
// 处理这些 BeanDefinition
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注册到 registry 中
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
这里我们关注 findCandidateComponents 方法:此方法完成了组件扫描的核心工作
ClassPathScanningCandidateComponentProvider # findCandidateComponents ()
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}
进入 scanCandidateComponents() :
这里我们也可以看到,通过组件扫描得到 bean 的 BeanDefinition 类型是:ScannedGenericBeanDefinition 类型
TypeFilter
当我们使用的是默认的组件扫描策略时:
protected void registerDefaultFilters() {
// 向 includeFilters 集合中加入:AnnotationTypeFilter(Component.class)
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
// 根据条件决定要不要向 includeFilters 集合中加入 JSR-330 规范的:AnnotationTypeFilter(ManagedBean.class)、AnnotationTypeFilter(Named.class)
try {
this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
//...
}
try {
this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
//...
}
}
向 includeFilters 集合中加入了一个TypeFilter:AnnotationTypeFilter (按照类上的注解过滤),AnnotationTypeFilter(Component.class) ,表示若类上加了 @Component 注解,则表示当前类是候选组件
其它常见的 TypeFilter 有:AssignableTypeFilte(按照类的类型过滤)。也可以自定义 TypeFilter 重写 match() 方法,来自定义匹配规则,如:
public class MyTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 通过 MetadataReader 可以获取当前类的元信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 如果类名包含 "ServiceImpl", 则表示匹配
String className = classMetadata.getClassName();
if (className.contains("ServiceImpl")){
return true;
}
return false;
}
}
ClassPathBeanDefinitionScanner 有两个重要的成员属性:(定义在父类 ClassPathScanningCandidateComponentProvider 中)
List< TypeFilter> includeFilters 、List< TypeFilter> excludeFilters
可以通过API:addIncludeFilter()、addExcludeFilter()向两个集合中添加相应的TypeFilter完成自定义过滤逻辑
如果 excludeFilters 中有一个 TypeFilter 的 match() 方法返回 true ,则表示当前组件不是候选组件,直接返回 false
如果 excludeFilters 中所有的 TypeFilter 的 match() 方法均返回 false,说明当前组件"不是需要排除的组件" ,那么还需要继续判断:当前组件是不是我们期望的组件
如果 includeFilters 中有一个 TypeFilter 的 match() 方法返回 true ,则表明当前组件是候选组件
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
// 若this.excludeFilters中有一个 TypeFilter 的 match 方法返回 true,则表示不是候选bean
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
// 若this.includeFilters中有一个 TypeFilter 的 match 方法返回 true,则调用 isConditionMatch() (一般都会返回true)
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
测试案例:
测试1:扫描 “aom.spring.bean” 包下的所有组件
@Test
public void test1() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 不使用默认过滤策略
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory, false);
// 向 includeFilters 集合中添加自定义 TypeFilter
// 一律返回true
scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
scanner.scan("aom.spring.bean");
// 测试输出:
String[] bdns = beanFactory.getBeanDefinitionNames();
for (String bdn : bdns) {
System.out.println(bdn);
}
}
测试2:扫描 “aom.spring.bean” 包下 “除了带有@Configuration的组件,其它的所有组件”
@Test
public void test2() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 不使用默认过滤策略
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory, false);
// 向 includeFilters 集合中添加自定义 TypeFilter (先扫描所有)
scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
// 向 excludeFilters 集合中添加注解的 TypeFilter(再排除特定)
scanner.addExcludeFilter(new AnnotationTypeFilter(Configuration.class));
scanner.scan("aom.spring.bean");
// 测试输出:
String[] bdns = beanFactory.getBeanDefinitionNames();
for (String bdn : bdns) {
System.out.println(bdn);
}
}
测试3:只扫描 “aom.spring.bean” 包下带有 @Configuration 的组件
@Configuration@Test
public void test3() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 不使用默认过滤策略
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory, false);
// 向 includeFilters 集合中添加自定义 TypeFilter
scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
// 若类上标注有 @Configuration 注解,则返回true
if (metadata.hasAnnotation(Configuration.class.getName())) {
return true;
}
return false;
});
scanner.scan("aom.spring.bean");
// 测试输出:
String[] bdns = beanFactory.getBeanDefinitionNames();
for (String bdn : bdns) {
System.out.println(bdn);
}
}
测试4:只扫描 "aom.spring.bean"包下的类名包含"User"字符串的组件
@Test
public void test4() {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory, false);
// 向 includeFilters 集合中添加自定义 TypeFilter
scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> {
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// ClassMetadata:类的元信息(是通过ASM技术直接读取的.class文件)
String className = classMetadata.getClassName();
// 只有类名包含"User"的才能成为候选组件
if (className.contains("User")) {
return true;
}
return false;
});
scanner.scan("aom.spring.bean1");
// 测试输出:
String[] bdns = beanFactory.getBeanDefinitionNames();
for (String bdn : bdns) {
System.out.println(bdn);
}
}
【附】 @ComponentScan 原理
先回顾一下 @ComponentScan 的基本使用:
配置1:以下配置表示,只有:UserService 类型的组件,才会被扫描到
@ComponentScan(value="com.springdemo",
useDefaultFilters = false, // 不使用默认过滤规则
includeFilters={
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = UserService.class),
}
)
配置2:以下配置表示,只有:加了@Controller 注解的组件,才会被扫描到
@ComponentScan(value="com.springdemo",
useDefaultFilters = false, // 不使用默认过滤规则
includeFilters={
@Filter(type = FilterType.ANNOTATION, classes = Controller.class)
}
)
配置3:以上配置表示,只有:类名含有 “ServiceImpl” 的组件,才会被扫描到
@ComponentScan(value="com.springdemo",
useDefaultFilters = false, // 不使用默认过滤规则
includeFilters={
@Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class)
}
)
其中自定义的 MyTypeFilter 实现:
public class MyTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 如果类名包含 "ServiceImpl", 则表示匹配
String className = classMetadata.getClassName();
if (className.contains("ServiceImpl")){
return true;
}
return false;
}
}
使用 @Filter 指定扫描规则
@Filter(type=FilterType.ANNOTATION, classes={Service.class}) // 是标有@Service注解标注的类
@Filter(type=FilterType.ASSIGNABLE_TYPE, classes={UserService.class}) // 是UserService类型的类
@Filter(type=FilterType.CUSTOM, classes={MyTypeFilter.class}) // 自定义TypeFilter
@Filter(type=FilterType.ASPECTJ, classes={AspectJTypeFilter.class}) //
@Filter(type=FilterType.REGEX, classes={RegexPatternTypeFilter.class}) // 正则表达式相关(没用过...)
所以可以有以下几种过滤策略:
public enum FilterType {
ANNOTATION, // 按照注解过滤
ASSIGNABLE_TYPE, // 按照指定类型过滤
ASPECTJ,
REGEX,
CUSTOM // 自己实现TypeFilter进行自定义规则
}
原理:
【前置知识】:在 Spring 容器的刷新 refresh() 过程中,在 invokeBeanFactoryPostProcessors() 一步中,会激活 ConfigurationClassPostProcessor 这个 bean工厂后置处理器,向IOC容器中添加 BeanDefinition。
它会经过一系列复杂的解析过程,解析带有 @Configuration、@Component、@ComponentScan、@Import、@ImportResource、@Bean…的类或方法,其中 @ComponentScan 的解析交由 ComponentScanAnnotationParser # parse 完成:
根据 @Filter 中的配置创建相应的 TypeFilter:
【附】 Spring 整合 MyBatis 中使用的 ClassPathMapperScanner
【快速回顾】:MapperScannerConfigurer 可以为一个包下的所有 Mapper 接口创建代理,并注入Spring 容器
MapperScannerConfigurer 实现了 BeanDefinitionRegistryPostProcessor 接口,它的 postProcessBeanDefinitionRegistry() 方法用于向 Spring 容器中添加 BeanDefinition:
MapperScannerConfigurer # postProcessBeanDefinitionRegistry
其中 ClassPathMapperScanner 是 ClassPathBeanDefinitionScanner 的子类,专门用于扫描 @Mapper 接口,分析它的扫描逻辑:
测试案例:
@Test
public void test(){
/*
使用 ClassPathMapperScanner (仿照 MapperScannerConfigurer # postProcessBeanDefinitionRegistry)
*/
// 不使用默认的过滤策略
ClassPathMapperScanner cpms = new ClassPathMapperScanner(beanFactory);
// 只扫描标有@Mapper注解的组件
cpms.setAnnotationClass(Mapper.class);
// 初始化 includeFilters、excludeFilters 集合
cpms.registerFilters();
cpms.scan("aom.spring.mapper");
String[] bdns = beanFactory.getBeanDefinitionNames();
for (String bdn : bdns) {
System.out.println(bdn);
}
}
注:在 MyBatis 整合 SpringBoot 时,自动配置类 MybatisAutoConfiguration 中用的也是类似这种方式(可以翻看源码)。
所以在 SpringBoot 项目中,我们只要在 Mapper 接口上加上 @Mapper 注解,那么底层就会自动识别并注入到容器中
未完,待…
…
…
…
…