springboot启动流程(八)ioc容器refresh过程(下篇)

所有文章

https://www.cnblogs.com/lay2017/p/11478237.html

 

正文

上一篇文章,我们知道了解析过程将从解析main方法所在的主类开始。在文章的最后我们稍微看了一下ConfigurationClassParser这个解析器的parse方法

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

本文将从这个parse方法继续下去,看看解析main方法所在的主类这个过程主要发生了什么。

 

跟进processConfigurationClass方法

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    // 

    // 由main方法所在的主类开始,向超类逐层向上递归解析
    SourceClass sourceClass = asSourceClass(configClass);
    do {
        // 这里包含了解析单个配置类的核心逻辑
        sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    } while (sourceClass != null);

    // 
}

我们注意到,doProcessConfigurationClass方法将会完成解析的主要工作,但是又会返回一个新的sourceClass用于解析。而这个新的sourceClass会是当前上一个sourceClass的父类。所在解析过程是一个递归过程,由主类开始,向超类逐层向上递归解析处理。

 

继续跟进doProcessConfigurationClass方法,我们看看这个核心的解析逻辑。代码量对较多,我们只关注两个点

1)@ComponentScan注解解析,扫描并注册BeanDefinition

2)获取超类向上递归

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {
    //

    // 处理@ComponentScan注解
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
            !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        // 遍历@ComponentScan的属性值
        for (AnnotationAttributes componentScan : componentScans) {
            // 解析扫描
            Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // 
        }
    }

    // 

    // 判断是否有超类
    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            // 返回待解析的超类
            return sourceClass.getSuperClass();
        }
    }

    // 没有超类,则解析完毕
    return null;
}

首先我们main方法所在的主类是被@SpringbootApplication注解所标注的,而@SpringbootApplication组合了@ComponentScan。所谓解析主类的时候将会处理@ComponentScan注解。解析@ComponentScan的主要工作的实现由ComponentScanAnnotationParser这个解析器来完成。通常这个解析器完成之后,被扫描到的BeanDefinition将会被注册到BeanFactory当中。

doProcessConfigurationClass方法的最后一部分是从当前被解析的类元数据中获取超类,如果超类存在且需要被解析那么就当做返回值返回回去,从而被外层的方法给递归处理。

 

@ComponentScan注解解析

下面,我们跟进ComponentScanAnnotationParser这个解析器的parse方法,看看@ComponentScan的处理过程

public Set<BeanDefinitionHolder> parse(
    AnnotationAttributes componentScan, 
    final String declaringClass) {
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

    //

    Set<String> basePackages = new LinkedHashSet<>();
    // 从basePackages配置获取扫描路径
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
        String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        Collections.addAll(basePackages, tokenized);
    }
    // 从basePackageClasses获取扫描路径
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
        basePackages.add(ClassUtils.getPackageName(clazz));
    }
    if (basePackages.isEmpty()) {
        // 默认添加当前被解析类的路径作为根路径
        basePackages.add(ClassUtils.getPackageName(declaringClass));
    }

    // 

    // 扫描目标路径
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}    

这里获取了一个扫描器,然后找到了待扫描的路径,最后利用扫描器去扫描路径。

 

跟进doScan方法

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        // 扫描获取BeanDefinition
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            //

            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                // 注册BeanDefinition到BeanFactory
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

我们看到,findCandidateComponents方法将会根据扫描路径获取BeanDefinition,而扫描出来的BeanDefinition将会进入注册方法registerBeanDefinition。

 

我们先跟进findCandidateComponents方法看看如何扫描获取

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    } else {
        // 进入
        return scanCandidateComponents(basePackage);
    }
}

再跟进scanCandidateComponents方法

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        // 拼接出搜索路径,例如:classpath*:cn/lay/springbootlearn/**/*.class
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // 获取搜索路径下待处理资源
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        // 
        for (Resource resource : resources) {
            // 
            if (resource.isReadable()) {
                try {
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    if (isCandidateComponent(metadataReader)) {
                        // 转化成BeanDefinition
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        // 
                    } else {
                        // 
                    }
                } catch (Throwable ex) {
                    // 
                }
            } else {
                // 
            }
        }
    } catch (IOException ex) {
        // 
    }
    return candidates;
}

basePackage将会被拼接成搜索路径,如:classpath*:cn/lay/springbootlearn/**/*.class。而getResources方法将会从搜索路径中获取相应的资源对象,这些资源对象并最终被读取并转化为BeanDefinition。

到这里,@ComponentScan扫描的Bean就已经成为了BeanDefinition,但是还有一步就是将BeanDefinition注册到BeanFactory当中。

 

我们回到doScan方法,并跟进registerBeanDefinition方法,看看注册过程

protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}

继续跟进registerBeanDefinition

public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder, 
        BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {

    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    // 省略
}

我们看到这里直接注册到了BeanDefinitionRegistry中去了,其实就是注册到BeanFactory当中。BeanFactory的默认实现类DefaultListableBeanFactory实现了BeanDefinitionRegistry,所以DefaultListableBeanFactory即是BeanDefinition的注册位置。

 

跟进DefaultListableBeanFactory的registerBeanDefinition方法

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

public void registerBeanDefinition(
        String beanName, 
        BeanDefinition beanDefinition) throws BeanDefinitionStoreException {

    // 
    if (existingDefinition != null) {
        //
    } else {
        if (hasBeanCreationStarted()) {
            // Cannot modify startup-time collection elements anymore (for stable iteration)
            synchronized (this.beanDefinitionMap) {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                // 
            }
        } else {
            //
        }
        //
    }
    // 
}

最终,也就是将BeanDefinition添加到一个key-value的集合当中,这样就完成了注册工作。

 

总结

到这里,ioc容器refresh过程部分就结束了。我们略过不少东西,将解析主类、解析@ComponentScan扫描Bean定义、注册到BeanFactory这个主要的流程过了一遍。当然,在这里可能还存在一个比较困惑的点。前面的文章中,我们提过几次:配置 -> BeanDefinition -> Bean这样一个过程。ioc的refresh过程却只有从配置 -> BeanDefinition这样一个过程,那么BeanDefinition -> Bean这个过程又在哪里呢?后面ioc容器注入部分将说明这部分内容。

 

posted @ 2019-09-11 00:37  __lay  阅读(1671)  评论(0编辑  收藏  举报