Spring注解扫描-@ComponentScan底层原理分析

Spring注解扫描-@ComponentScan底层原理分析

 

 

1、@ComponentScan 简介

        @ComponentScan 注解的作用可以简述为:将项目中所有被 @Component 注解直接或者间接标记的类组装成BeanDefinition, 然后以Map<beanName, BeanDefinition>的形式存储,为后续生成bean对象做准备。 继承了@Component的注解包括:@Controller、@RestController、@Repository、@Service、@Configuration等。 

@ComponentScan重要参数: 

  1. value:用来指定basePackage的路径; 
  2. excludeFilters:可以把被@Component标识的类排除扫描; 
  3. includeFilters:可以把不被@Component标识的类加入到扫描; 

示例:

@ComponentScan(value = "com.dw",
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = UserService.class)},
        includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = User.class)})

 

2、在讲源码之前呢,我们可以先增强几个知识点的概念。这些知识点在阅读Spring扫描源码中起着关键作用。

2.1、被@ComponentScan扫描注入到IOC容器中的BeanDefinition,其具体实现类是:ScannedGenericBeanDefinition。 

2.2、@ComponentScan扫描涉及到的相关注解:@Conditional和@Scope和@Lookup和@Lazy、@Primary、@DependsOn、@Role、@Description。 

@Conditional的作用是:即使该类被@Component修饰,但是Conditional返回false,该类也不会被成功扫描; @Scope的作用是:仅仅作为一个标识,赋值给BeanDefinition的scope属性; @Lookup:抽象类本身不能被扫描进容器,但是被@Lookup注解修饰则可以; @Lazy、@Primary、@DependsOn、@Role:这几个类是一起被处理的,也只是用来给BeanDefinition赋值,分别对应着:setLazyInit(boolean)、setPrimary(boolean)、setDependsOn(value)、setRole(value)、setDescription(value); 

2.3、须知Resource类和MetadataReader类。 Resource[]数组对象会存储basePackage包路径下所有的class的文件对象(注意:是所有的class的文件对象都会存储,无论有没有被@Component修饰); MetadataReader类是Spring用来解析类的信息(比如类名、类中的方法、类上的注解、是否是抽象类…,这些都可以称之为类的元数据)的一个工具类。而 ClassMetadata、AnnotationMetadata都用来封装解析后的类信息。

 

3、源代码调用链路分析

     Spring配置类的解析都是通过ConfigurationClassPostProcessor 这个类处理的, 而这个类实现了BeanDefinitionRegistryPostProcessor接口, 这个接口主要是Spring容器初始化时, 对IOC容器中的Bean的增删改操作。 具体的调用链路如下:

org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()--> #processConfigBeanDefinitions()
-->
org.springframework.context.annotation.ConfigurationClassParser#parse(Set<BeanDefinitionHolder>)
-->
org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass()--> #doProcessConfigurationClass()
-->
org.springframework.context.annotation.ComponentScanAnnotationParser#parse()
-->
org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan()--> #doScan()

 

4、核心源码分析

源码具体位置:org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
            Assert.notEmpty(basePackages, "At least one base package must be specified");
            Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
            // 遍历basePackages
            for (String basePackage : basePackages) {

                // 扫描basePackages下所有的文件,进行include、exclude、@Conditional判断后返回BeanDefinition
                Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
                // 循环处理candidates设置相关的属性值, 然后注册BeanDefinition
                for (BeanDefinition candidate : candidates) {
                // scope解析:单例与多例
                    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);  
                    candidate.setScope(scopeMetadata.getScopeName());
                // 生成beanName:默认是类名首字母小写,如果@Component中指定了name则使用指定的name
                    String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); 
                    /* ScannedGenericBeanDefinition extends GenericBeanDefinition(AbstractBeanDefinition) 
                    implements AnnotatedBeanDefinition  */
                    // 设置BeanDefinition的默认值
                    if (candidate instanceof AbstractBeanDefinition) {
                        postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                    }
                    if (candidate instanceof AnnotatedBeanDefinition) {
                        // 解析@Lazy、@Primary、@DependsOn、@Role、@Description
                        AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                    }

                    // 检查Spring容器中是否已经存在该beanName(如果存在则抛出异常)
                    if (checkCandidate(beanName, candidate)) {
                        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                        definitionHolder =
                                AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                        beanDefinitions.add(definitionHolder);

                        // 注册: beanDefinitionMap.put(beanName, beanDefinition)
                        registerBeanDefinition(definitionHolder, this.registry);
                    }
                }
            }
            return beanDefinitions;
        }

     上面doScan()方法的核心其实是 findCandidateComponents(basePackage)--> scanCandidateComponents(basePackage) 这个方法, 主要负责basePackage包下资源文件的读取(PathMatchingResourcePatternResolver), 判断是否时候选的Bean, 如果是则会创建一个ScannedGenericBeanDefinition。 并添加到集合中,最终返回这个集合, 会注入到IOC容器中。

 

posted @ 2023-11-11 19:29  邓维-java  阅读(2758)  评论(0)    收藏  举报