关于java:Spring源码解析之-ConfigurationClassPostProcessor

6次阅读

共计 9275 个字符,预计需要花费 24 分钟才能阅读完成。

Spring 源码解析之 ——ConfigurationClassPostProcessor

思考:

(1) @Configuration,@Component,@Bean,@Import 注解的作用是什么? 什么时候被解析的。

这里就 ConfigurationClassPostProcessor 就上场了

先看张图,这个就是 ConfigurationClassPostProcessor 解决的构建节点。ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor,
而 BeanDefinitionRegistryPostProcessor 继承了 BeanFactoryPostProcessor,所以 ConfigurationClassPostProcessor 中须要重写 postProcessBeanDefinitionRegistry()
办法和 postProcessBeanFactory()办法。而 ConfigurationClassPostProcessor 类的作用就是通过这两个办法去实现的。

ConfigurationClassPostProcessor 做些什么?

1:postProcessBeanDefinitionRegistry()将 @Configuration,@Component,@Bean,@Import 等注解的对象, 解析为 BeanDefinition, 放在容器中,期待被解析为 Spring bean;
这里会将 @Configuration 注解的类的 BeanDefinition 增加个属性,标记是 FULL,还是 LITE
2:postProcessBeanFactory()被标记 FULL 的 BeanDefinition 的 beanClass 替换为 Cglib 代理的 class 名

processConfigBeanDefinitions()代码解析

  public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
        String[] candidateNames = registry.getBeanDefinitionNames();

        //..... 省略


        // 将候选者进行排序
        configCandidates.sort((bd1, bd2) -> {int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
            int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
            return Integer.compare(i1, i2);
        });

        // 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(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
                if (generator != null) {
                    this.componentScanBeanNameGenerator = generator;
                    this.importBeanNameGenerator = generator;
                }
            }
        }

        if (this.environment == null) {this.environment = new StandardEnvironment();
        }

        // 创立 @Configuration 解析器
        ConfigurationClassParser parser = new ConfigurationClassParser(
                this.metadataReaderFactory, this.problemReporter, this.environment,
                this.resourceLoader, this.componentScanBeanNameGenerator, registry);

        Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
        Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
        do {StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
            // 解析配置类,在此处会解析配置类上的注解(ComponentScan 扫描出的类,@Import 注册的类,以及 @Bean 办法定义的类)
            // 留神:这一步只会将加了 @Configuration 注解以及通过 @ComponentScan 注解扫描的类才会退出到 BeanDefinitionMap 中
            // 通过其余注解 (例如 @Import、@Bean) 的形式,在 parse()办法这一步并不会将其解析为 BeanDefinition 放入到 BeanDefinitionMap 中,// 而是先解析成 ConfigurationClass 类
            // 真正放入到 map 中是在上面的 this.reader.loadBeanDefinitions()办法中实现的
            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 解析出的 ConfigurationClass 类加载成 BeanDefinition
            // 实际上通过上一步的 parse()后,解析进去的 bean 曾经放入到 BeanDefinition 中了,然而因为这些 bean 可能会引入新的 bean,例如实现了 ImportBeanDefinitionRegistrar 或者 ImportSelector 接口的 bean,或者 bean 中存在被 @Bean 注解的办法
            // 因而须要执行一次 loadBeanDefinition(),这样就会执行 ImportBeanDefinitionRegistrar 或者 ImportSelector 接口的办法或者 @Bean 正文的办法
            this.reader.loadBeanDefinitions(configClasses);
            alreadyParsed.addAll(configClasses);
            processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();

            candidates.clear();
            //.... 省略代码
        }
        while (!candidates.isEmpty());
        //..... 省略代码
    }

1.parser.parse()

依据 BeanDefinition 类型的不同,调用 parse()不同的重载办法。理论是调用 ConfigurationClassParser 中的 processConfigurationClass()办法
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());
                }
            }
            catch (BeanDefinitionStoreException ex) {throw ex;}
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
            }
        }

        this.deferredImportSelectorHandler.process();}

processConfigurationClass()办法中理论解析的办法为 doProcessConfigurationClass()办法

    protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
        //..... 省略代码
        
        SourceClass sourceClass = asSourceClass(configClass, filter);
        do {sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
        }
        while (sourceClass != null);

        this.configurationClasses.put(configClass, configClass);
    }

doProcessConfigurationClass()办法中就开始了 Spring 一些注解的解析

    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
                throws IOException {
    
            // 解析 @Component 注解的类
            if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
                // 遍历被注解类的外部类,这个也是为什么一些外部类一样会被 spring 获取到的起因
                processMemberClasses(configClass, sourceClass, filter);
            }
    
            // 解析 @PropertySource 注解
            for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,
                    org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);
                } else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                            "]. Reason: Environment must implement ConfigurableEnvironment");
                }
            }
    
            // 解析 @ComponentScans , @ComponentScan 注解
            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) {
                    // 解析 @ComponentScans , @ComponentScan 扫描包下的 @Component,@Service,@Controller,@Repository 注解。// 将扫描进去的类转为 bd 增加到 beanDefinitionMap 中
                    Set<BeanDefinitionHolder> scannedBeanDefinitions =
                            this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
                    // 对 @Configuration 注解进行非凡解决
                    for (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                        if (bdCand == null) {bdCand = holder.getBeanDefinition();
                        }
                        //checkConfigurationClassCandidate 给 @Configuration 注解的类打上 FULL 标记
                        if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());
                        }
                    }
                }
            }
    
            // 解析 Import 注解注册的 bean,这一步只会将 import 注册的 bean 变为 ConfigurationClass, 不会变成 BeanDefinition
            processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
    
            // 引入配置文件
            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);
                }
            }
    
            // 解析 @Bean 注解
            Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
            for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
            }
            //...... 省略代码
            // No superclass -> processing is complete
            return null;
        }

componentScanParser.parse()在扫描时会通过 includeFilters 和 excludeFilters 条件去决定具体扫描的 bean, 然而理论 includeFilters 中只有
@Component 注解,然而理论中扫描的到的 bean, 也会将 @Service,@Controller,@Repository,@Controller 都会被扫描进去。

这里大略是 @Component 元注解,Spring 在读取 @Service,@Controller,@Repository,@Controller,也读取了它的元注解,并将作为 @Component 解决。这里就不做具体分析了。

2.this.reader.loadBeanDefinitions()

在执行 parse()办法后 @Component,@Service 等注解的 bean 会被注解增加到 beanDefinitionMap, 而 @Import,@Bean 这些会被转为 ConfigurationClass 进行下一步解析。
这里是 loadBeanDefinitions()外面外围办法

    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;
        }
        // 注册 @Import 润饰的 bean
        if (configClass.isImported()) {registerBeanDefinitionForImportedConfigurationClass(configClass);
        }
        // 注册 @Bean 润饰的 bean
        for (BeanMethod beanMethod : configClass.getBeanMethods()) {loadBeanDefinitionsForBeanMethod(beanMethod);
        }
        // 注册 @ImportResources 配置的信息
        loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
        //@Import 注解的 bean, 且 bean 实现了 ImportBeanDefinitionRegistrar
        loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
    }

3.postProcessBeanFactory()办法

postProcessBeanFactory()对加了 @Configuration 注解的类进行 CGLIB 代理和向 Spring 中增加一个后置处理器 ImportAwareBeanPostProcessor。

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        //..... 省略代码
        
        //@Configuration 注解的类进行 CGLIB 代理
        enhanceConfigurationClasses(beanFactory);
        // 增加一个后置处理器 ImportAwareBeanPostProcessor
        beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
    }

正文完
 0