美文网首页
ConfigurationClassPostProcessor解

ConfigurationClassPostProcessor解

作者: hello_kd | 来源:发表于2020-08-23 20:49 被阅读0次
ConfigurationClassParser parser = new ConfigurationClassParser(
      this.metadataReaderFactory, this.problemReporter, this.environment,
      this.resourceLoader, this.componentScanBeanNameGenerator, registry);

ConfigurationClassPostProcessor读取BeanDefinition内部通过ConfigurationClassParser解析器来解析的

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());
            }
            
    }
    //解析完之后,再处理deferredImportSelector
    this.deferredImportSelectorHandler.process();
}

将待解析的配置类包装成一个ConfigurationClass类,这个类有这三个重要属性

AnnotationMetadata metadata  当前配置类的注解元数据对象
Resource resource 当前配置类的源
Set<ConfigurationClass> importedBy 当前配置类被谁导进来解析的

且还会传入一个filter,Predicate<String> DEFAULT_EXCLUSION_FILTER = className ->
(className.startsWith("java.lang.annotation.") || className.startsWith("org.springframework.stereotype."))

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
        //判断是否需要跳过不处理,主要是看配置类有没注解了@Conditional,且条件是满足的
        if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
        }
        // 判断当前配置是否已经被解析过了,若存在,说明之前解析过了,下面的这个逻辑最主要的判断就是,当前要解析的类是否为被导入的,若不是,优先进行解析,若是
        // 则不再进行解析了
        ConfigurationClass existingClass = this.configurationClasses.get(configClass);
        if (existingClass != null) {
        // 假如当前要解析的配置类是通过导入的方式(导入的方式主要有两种,一种是内部类,一种是通过@Import),且被解析过的也是通过导入的方式,那么直接
        // 合并两者的importedBy即可
        if (configClass.isImported()) {
        if (existingClass.isImported()) {
        existingClass.mergeImportedBy(configClass);
        }
        // 如果被解析过的类不是通过导入的,而当前要解析的是通过导入的,那么步处理,直接return
        return;
        }
        else {
        // 假如当前要解析的配置类不是被导入进来的,那么直接移除旧的,重新进行解析
        this.configurationClasses.remove(configClass);
        this.knownSuperclasses.values().removeIf(configClass::equals);
        }
        }

        // 递归处理配置类和他的父类
        SourceClass sourceClass = asSourceClass(configClass, filter);
        do {
        //每次解析完当前的配置类,会返回其继承的父类,若父类不为空,则继续递归解析,若为空了,则不解析
        sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
        }
        while (sourceClass != null);
        //将每次解析完的配置类添加到map中
        this.configurationClasses.put(configClass, configClass);
        }

doProcessConfigurationClass方法是解析配置类的核心,主要分为以下几个步骤

  1. 若配置类注解了@Component,那么也会解析其内部类
  2. 处理 @PropertySource,本文不做说明
  3. 处理@ComponentScan,扫描这个路径下的BeanDefinition
  4. 处理@Import注解
  5. 处理@ImportResource,这个一般是和xml相关的,本文不做说明
  6. 处理配置类内部注解了@Bean的方法
  7. processInterfaces,处理接口
  8. 处理父类,返回进行递归解析

步骤1,这种针对的是配置类有内部类的情况,会将内部类也当成配置类,进行递归解析

@Configuration
public class CoreConfig {
    //当解析MemberClassConfig1这个ConfigurationClass类时,会将外部类CoreConfig添加到其属性importedBy集合中
    class MemberClassConfig1 {
        @Bean
        public User user() {
            return new User()
        }
    }
}

步骤3,解析@ComponentScan,若该注解没有显示的设定属性值,那么默认会扫描配置类所在的路径下的所有BeanDefinition

//获取注解了@ComponentScan的列表
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
        sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
//这里再次判断是否需要跳过,看有没注解了@Conditional且条件符合的
if (!componentScans.isEmpty() &&
        !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
    //循环遍历每个@ComponentScan,解析对应的@BeanDefinition
    for (AnnotationAttributes componentScan : componentScans) {
        //获取到ComponentScan扫描路径下的所有BeanDefinition
        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();
            }
            //判断每个BeanDefinition是否还是一个配置类,若是的话,需要进行递归解析。应用程序中定义的bean,一般都符合配置类的条件
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
        }
    }
}
//这里判断BeanDefinition是否为一个配置类,主要通过以下3个方面,满足其中任何一种即可
//1. 是否注解了@Configuration,且该注解属性值proxyBeanMethods不为False
//2. 不是接口,且注解了@Import @Component @ImportResource @ComponentScan中的任何一个
//3. 不是接口,且类内部是否有注解了@Bean的方法

步骤4,处理@Import,springboot很多扩展都是直接或间接的通过@Import来实现的,Import类主要分为三种情况,分别为ImportSelector、ImportBeanDefinitionRegistrar、普通的配置类

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
        Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
        boolean checkForCircularImports) {
    //这里省略一些判断是否为空、是否循环import的代码,将主要关注点放在解析Import
    else {
        this.importStack.push(configClass);
        try {
            for (SourceClass candidate : importCandidates) {
                if (candidate.isAssignable(ImportSelector.class)) {
                    // ImportSelector -->  ImportSelector主要是处理这种情况的,如果应用要导入外部的10个@Configuration配置类,可以通过在                        // Import的value值写下这10个类,或者通过ImportSelector,只要实现了selectImports这个接口(返回这10个配置类),
                    // 然后通过@Import一个ImportSelector类,间接的将10个配置类导入进来解析,当然了,每个导入的配置类,还需要递归调用
                    // processImports,因为有可能这个配置类也是一个ImportSelector或ImportBeanDefinitionRegistrar
                    Class<?> candidateClass = candidate.loadClass();
                    ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                            this.environment, this.resourceLoader, this.registry);
                    Predicate<String> selectorFilter = selector.getExclusionFilter();
                    if (selectorFilter != null) {
                        exclusionFilter = exclusionFilter.or(selectorFilter);
                    }
                    //DeferredImportSelector继承自ImportSelector,用于在所有的@Configuration都解析完了之后,再处理这种延迟的Selector
                    //这个主要处理注解了@Conditional的
                    if (selector instanceof DeferredImportSelector) {
                        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                    }
                    else {
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                        //递归解析Import
                        processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                    }
                }
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    // ImportBeanDefinitionRegistrar -> 通过实现registerBeanDefinitions这个接口,可以手动注册更多的BeanDefinition
                    // 这里只是将ImportBeanDefinitionRegistrar添加到ConfigurationClass的属性importBeanDefinitionRegistrars中,key为
                    // ImportBeanDefinitionRegistrar,值为导入ImportBeanDefinitionRegistrar的所属配置类
                    Class<?> candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar =
                            ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                    this.environment, this.resourceLoader, this.registry);
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                else {
                    // Import类不是ImportSelector或者ImportBeanDefinitionRegistrar -> 当做@Configuration类进行递归解析
                    //candidate封装成ConfigurationClass进行解析,这类的逻辑和处理内部类的情况一样
                    this.importStack.registerImport(
                            currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
                }
            }
        }
        //省略异常处理逻辑
        finally {
            this.importStack.pop();
        }
    }
}

步骤6,处理@Bean方法。解析配置类的所有@Bean方法,每个@Bean方法会生成一个BeanMethod对象,然后添加到ConfigurationClass的属性集合beanMethods中

步骤7,处理实现的接口。在java8之后,接口有默认方法,因此这里需要判断实现的接口是否有注解了@Bean的默认方法,若有,也需要将这些默认方法生成对应的BeanMethod对象,添加到ConfigurationClass的属性集合beanMethods中

步骤8,处理父类。

if (sourceClass.getMetadata().hasSuperClass()) {
    //拿到当前配置类的父类,没显示extend的话,默认情况都是java.lang.Object
    String superclass = sourceClass.getMetadata().getSuperClassName();
    //若父类是Object类的话,或者knownSuperclasses包含了,那么直接返回null,无需解析父类
    if (superclass != null && !superclass.startsWith("java") &&
            !this.knownSuperclasses.containsKey(superclass)) {
        //将父类添加到knownSuperclasses,防止重复解析,因为可能会有多个配置类继承同一个父类的情况,那么其中一个解析,其他就不能重复解析了
        this.knownSuperclasses.put(superclass, configClass);
        return sourceClass.getSuperClass();
    }
}

当解析完所有的ConfigurationClass后,通过@ComponentScan扫描的BeanDefinition已经加入到容器中了,而通过其他方式的(@BeanMethod、@Import、@ImportResource)等,只是将一些中间对象维护到ConfigurationClass对象的某些属性值中,因此spring工厂还需要对这些进行处理,解析最终需要的BeanDefinition

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    //循环对每个ConfigurationClass进行处理,加载BeanDefinition对象
    for (ConfigurationClass configClass : configurationModel) {
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}
private void loadBeanDefinitionsForConfigurationClass(
        ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
    //这里再次判断配置类是否需要跳过,不处理
    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        String beanName = configClass.getBeanName();
        if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
            this.registry.removeBeanDefinition(beanName);
        }
        this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        return;
    }

    if (configClass.isImported()) {
        //将配置类自身转换成BeanDefinition对象,然后添加到容器中
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        //将BeanMethod对象转成BeanDefinition对象,然后添加到容器中
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }
    //解析通过ImportResource注解导入的BeanDefinition
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    //解析通过@Import注解导入的ImportBeanDefinitionRegistrar的BeanDefinition
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

相关文章

网友评论

      本文标题:ConfigurationClassPostProcessor解

      本文链接:https://www.haomeiwen.com/subject/nanodktx.html