『深入学习 Spring Boot』(十二) Configuration 配置类解析

前言

这一小节,是学习 Spring Boot 配置类的处理。

这里面细节还是挺多的,越看越感觉自己功力还不够,需要多多加油。

还是先记录一个大概的处理流程。后面再继续探究。

配置类解析

配置类解析入口

配置类解析的入口,在 refresh()方法的中invokeBeanFactoryPostProcessors(beanFactory)

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    // .....
        }
    }
复制代码

继续进入到PostProcessorRegistrationDelegate # invokeBeanFactoryPostProcessors()方法中,有这样一段代码:

if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
             // 常规后处理器集合初始化
            List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
            // 注册处理器集合初始化
            List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
            // 遍历所有处理器
            for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                // 如果实现了 BeanDefinitionRegistryPostProcessor 进行注册 BeanDefinition 的处理
                if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                    BeanDefinitionRegistryPostProcessor registryProcessor =
                            (BeanDefinitionRegistryPostProcessor) postProcessor;
                    registryProcessor.postProcessBeanDefinitionRegistry(registry);
                    registryProcessors.add(registryProcessor);
                }
                else {
                    regularPostProcessors.add(postProcessor);
                }
            }
    ......
}
复制代码

这里,对 BeanDefinitionRegistryPostProcessor的实现,做了特殊的逻辑处理,其中有一个实现为ConfigurationClassPostProcessor

BeanFactoryPostProcessor 用于引导被 @Configuration 注解的类的处理。
这个后处理器是优先处理的。因为在任何其他BeanFactoryPostProcessor执行之前, 被@Configuration注解的类中声明的,任何被 @Bean 注解的方法,都必须注册其相应的 bean 定义,这一点很重要。

我们进入ConfigurationClassPostProcessor被调用的方法中:

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        int registryId = System.identityHashCode(registry);
        if (this.registriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                    "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
        }
        if (this.factoriesPostProcessed.contains(registryId)) {
            throw new IllegalStateException(
                    "postProcessBeanFactory already called on this post-processor against " + registry);
        }
        this.registriesPostProcessed.add(registryId);
        // 主要调用此方法
        processConfigBeanDefinitions(registry);
    }
复制代码

processConfigBeanDefinitions

继续进入到此方法中:

    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
        String[] candidateNames = registry.getBeanDefinitionNames();
 
        for (String beanName : candidateNames) {
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);
            if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                    ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
                ......
            }
             // 判断是否是配置类。如果是,设置 BeanDefiniton 的属性lite/full。
             // @Configuration --> full
             // @Bean、@Component、@ComponentScan、@Import、@ImportResource --> lite
            else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
                configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
            }
        }
 
        // Return immediately if no @Configuration classes were found
         // 如果未找到@Configuration类,则立即返回
        if (configCandidates.isEmpty()) {
            return;
        }
        ......
 
        // Detect any custom bean name generation strategy supplied through the enclosing application context
        SingletonBeanRegistry sbr = null;
        if (registry instanceof SingletonBeanRegistry) {
            sbr = (SingletonBeanRegistry) registry;
            if (!this.localBeanNameGeneratorSet) {
                BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
                ......
            }
        }
        ......
        // Parse each @Configuration class
        // 解析每个@Configuration 类
        ConfigurationClassParser parser = new ConfigurationClassParser(
                this.metadataReaderFactory, this.problemReporter, this.environment,
                this.resourceLoader, this.componentScanBeanNameGenerator, registry);
 
        // BeanDefiniton候选集合
        Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
        // 已解析 BeanDefiniton 集合
        Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
        do {
             // 解析 配置类中的 @Component、@PropertySources、@ComponentScans、@ImportResource、@Bean。
             // 只有 @Configuration 与 @ComponetScans 会被加载为 BeanDefinition
            parser.parse(candidates);
            parser.validate();
 
            Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
            configClasses.removeAll(alreadyParsed);
 
            // Read the model and create bean definitions based on its content
            if (this.reader == null) {
                this.reader = new ConfigurationClassBeanDefinitionReader(
                        registry, this.sourceExtractor, this.resourceLoader, this.environment,
                        this.importBeanNameGenerator, parser.getImportRegistry());
            }
            // parser.parse(candidates)解析出来的这些bean可能会引入新的bean,例如实现了ImportBeanDefinitionRegistrar或者ImportSelector接口的bean,或者 bean中存在被@Bean注解的方法。
            // 所以执行一次loadBeanDefinition()。用于解析 ImportBeanDefinitionRegistrar 或 ImportSelector接口 或@Bean 注释的方法。
            this.reader.loadBeanDefinitions(configClasses);
            alreadyParsed.addAll(configClasses);
 
            candidates.clear();
            .......
    }
复制代码

parser.parse(candidates)

这个方法是,解析配置类的核心方法。

// ConfigurationClassParser # parse
public void parse(Set<BeanDefinitionHolder> configCandidates) {
        for (BeanDefinitionHolder holder : configCandidates) {
            BeanDefinition bd = holder.getBeanDefinition();
            try {
                // 通过注解方式标识配置类,会进入这个方法中。
                if (bd instanceof AnnotatedBeanDefinition) {
                    parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
                }
                else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                    parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
                }
                else {
                    parse(bd.getBeanClassName(), holder.getBeanName());
                }
            }
            ......
    }
复制代码

进入重载方法:

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

processConfigurationClass():

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
        // 判断是否符合解析条件
        if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
            return;
        }
        ......
        // Recursively process the configuration class and its superclass hierarchy.
        // 递归处理配置类及其超类层次结构。
        // 等于说 doProcessConfigurationClass 是处理加载 Configuration 的核心递归逻辑。
        SourceClass sourceClass = asSourceClass(configClass);
        do {
            sourceClass = doProcessConfigurationClass(configClass, sourceClass);
        }
        while (sourceClass != null);
 
        this.configurationClasses.put(configClass, configClass);
    }
复制代码

doProcessConfigurationClass():

通过从源类中读取注解、成员和方法来应用处理并构建一个完整的ConfigurationClass 。

好!从这里开始,我们可以想想,一个 Configuration 类被开始解析了。

下面我们来看一下解析步骤:

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
            throws IOException {
 
        if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
            // Recursively process any member (nested) classes first
            // 1. 首先处理成员变量。如果成员里有配置类,就会 processConfigurationClass。如此形成了递归。
            processMemberClasses(configClass, sourceClass);
        }
 
        // Process any @PropertySource annotations
         // 2. 处理 @PropertySource,主要是加载属性到环境中
        for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
                sourceClass.getMetadata(), PropertySources.class,
                org.springframework.context.annotation.PropertySource.class)) {
            if (this.environment instanceof ConfigurableEnvironment) {
                processPropertySource(propertySource);
            }
            ....
        }
 
        // Process any @ComponentScan annotations
         // 3. 处理 @ComponentScan 注解。扫描配置类上配置的扫描路径中的 Bean。
        Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
                sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
        if (!componentScans.isEmpty() &&
                !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
            for (AnnotationAttributes componentScan : componentScans) {
                // The config class is annotated with @ComponentScan -> perform the scan immediately
                // 扫描 @ComponentScan
                // 这个方法去扫描配置的扫描范围。其实最开始时,加载的是我们的引导类。然后去扫描了引导类所在包的全部子包。
                // 然后把我们写的其他 Configuration 才被扫描到,加载进来。
                Set<BeanDefinitionHolder> scannedBeanDefinitions =
                        this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
                // Check the set of scanned definitions for any further config classes and parse recursively if needed
                 // 检查任何其他配置类的扫描定义集,并在需要时递归解析
                for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                    BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                    if (bdCand == null) {
                        bdCand = holder.getBeanDefinition();
                    }
                    // 如果扫描出来的是 Configuration ,就递归调用解析
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                        parse(bdCand.getBeanClassName(), holder.getBeanName());
                    }
                }
            }
        }
 
        // Process any @Import annotations
         // 4. 处理@Import注解。通过@Import注解,引入的配置类。
        // 这里也需要注意,后面的自动装配就是通过@Import作为入口的。
        processImports(configClass, sourceClass, getImports(sourceClass), true);
 
        // Process any @ImportResource annotations
        // 5. 处理@ImportResource注解,解析配置文件。
        // 这里需要注意,处理方式是 addImportedResource。也就是只赋值给了 configClass 的属性,并没有做其他处理。
        AnnotationAttributes importResource =
                AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        if (importResource != null) {
            String[] resources = importResource.getStringArray("locations");
            Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
            for (String resource : resources) {
                String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
                configClass.addImportedResource(resolvedResource, readerClass);
            }
        }
 
        // Process individual @Bean methods
        // 6. 处理被 @Bean 注释的方法
        Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
        for (MethodMetadata methodMetadata : beanMethods) {
            configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
        }
 
        // Process default methods on interfaces
         // 7. 处理接口的默认方法
        processInterfaces(configClass, sourceClass);
 
        // Process superclass, if any
        // 8. 类,类包名以java开头的除外
        if (sourceClass.getMetadata().hasSuperClass()) {
            String superclass = sourceClass.getMetadata().getSuperClassName();
            if (superclass != null && !superclass.startsWith("java") &&
                    !this.knownSuperclasses.containsKey(superclass)) {
                this.knownSuperclasses.put(superclass, configClass);
                // Superclass found, return its annotation metadata and recurse
                return sourceClass.getSuperClass();
            }
        }
 
        // No superclass -> processing is complete
        return null;
    }
复制代码

这个方法是,解析 配置类的核心方法,其中解析了@Component、@PropertySources、@ComponentScans、@ImportResource、@Bean,还解析了java8以后提供的默认方法。

这些个方法就不一个个细看了,第一次看源码,还是先把大概流程看明白叭。

总结

这一节,大致梳理了 Configuration 类的主干处理流程。

还有很多细节,留待学习。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享