美文网首页
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