Spring源码:《Spring的组件扫描》

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> includeFiltersList< 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 注解,那么底层就会自动识别并注入到容器中

未完,待…




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值