zoukankan      html  css  js  c++  java
  • SpringBoot自动配置原理

    1.什么是自动配置

    个人理解SpringBoot的自动配置就是在系统启动的过程中自动扫描加载starter和自定义的配置类和配置文件中的bean,并且能根据当前环境和条件动态加载bean,达到开箱即用的目的。

    2.从注解反向看自动配置

    说到自动配置,很多帖子会直接从启动类的main函数说起,从@SpringBootApplication这个入手,进而找到加载Bean的入口,一般情况下是可以这样看的。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
            @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {

    SpringBootApplication 这个注解是个复合注解,

    @SpringBootConfiguration 这个注解的作用和@Configuration的作用是一致的,声明当前类为一个配置类。

    @ComponentScan 这个注解的作用是声明Bean扫描的相关信息,扫描路径和排除,包含关系

    @EnableAutoConfiguration 字面意思是开启自动配置。它本身也是一个复合注解

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
    @AutoConfigurationPackage注解其实是向容器导入了一个Bean,
    @Import(AutoConfigurationImportSelector.class)向容器导入了AutoConfigurationImportSelector 这个Bean,这个Bean的作用是加载自动配置类
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return NO_IMPORTS;
            }
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                    .loadMetadata(this.beanClassLoader);
    //加载自动配置类 AutoConfigurationEntry autoConfigurationEntry
    = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
    protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
                AnnotationMetadata annotationMetadata) {
            if (!isEnabled(annotationMetadata)) {
                return EMPTY_ENTRY;
            }
            AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //获取候选的配置类名称,其实是加载包路径下的META-INF/spring.factories 中的类路径 List
    <String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    //排重 configurations
    = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions);
    //移除要排除的配置类 configurations.removeAll(exclusions);
    //筛选满足条件的配置类,这个地方其实是根据Condition过滤到满足条件的配置类 configurations
    = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }

    到此为止,我们仅仅能看到加载了哪些类,但是每个类中又有很多Bean,每个Bean上又加了很多Condition注解,那这些Bean是如何加载的?这个AutoConfigurationImportSelector是什么时候被触发的?如果单从注解反向看的话是很难回答的?

    3.从启动流程正向看自动配置

    要想真正了解自动配置原理,还是要从启动流程中下手,启动过程中有很重要的一步是

    refreshContext(context);
    它调用的是AbstractApplicationContext的 refresh方法, 启动有一步是执行BeanFactory的后置处理器
    invokeBeanFactoryPostProcessors(beanFactory);
    这里面有一段执行 BeanDefinitionRegistryPostProcessor 的代码,这个是和bean注册相关的
        // Do not initialize FactoryBeans here: We need to leave all regular beans
                // uninitialized to let the bean factory post-processors apply to them!
                // Separate between BeanDefinitionRegistryPostProcessors that implement
                // PriorityOrdered, Ordered, and the rest.
                List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
    
                // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.

    // 这个地方拿到的是org.springframework.context.annotation.internalConfigurationAnnotationProcessor 这个类, String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors);
            //currentRegistryProcessors中存放的是ConfigurationClassPostProcessor,这个类是自动配置的核心 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear();
    invoke 方法调用的org.springframework.context.annotation.ConfigurationClassPostProcessor的processConfigBeanDefinitions方法
    String[] candidateNames = registry.getBeanDefinitionNames();
    
            for (String beanName : candidateNames) {
                BeanDefinition beanDef = registry.getBeanDefinition(beanName);
                if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
                    }
                }
                else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
                    configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
                }
            }

    从候选bean中选取有@Configuration的类,候选的有下面几个,其中只有我们自己的主启动类包含了这个注解

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    conditionTestApplication(主启动类)
    org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory

    接下来执行parser,这里面循环调用,将所有@Configuration的配置类都处理了一遍,将使用@Bean注解的方法都提取出来了

    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 {
    //记载配置类 parser.parse(candidates); parser.validate();         //存放配置类及Bean信息 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()); }
    //根据条件注解,加载Bean
    this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses);
    parser.parse(candidates); 方法中调用了 this.deferredImportSelectorHandler.process(),而 AutoConfigurationImportSelector 是 DeferredImportSelector的实现类
    public void process() {
                List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
                this.deferredImportSelectors = null;
                try {
                    if (deferredImports != null) {
                        DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
                        deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
                        deferredImports.forEach(handler::register);
    //进行导入 handler.processGroupImports(); } }
    finally { this.deferredImportSelectors = new ArrayList<>(); } }
    public void processGroupImports() {
                for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
                    grouping.getImports().forEach(entry -> {
                        ConfigurationClass configurationClass = this.configurationClasses.get(
                                entry.getMetadata());
                        try {
                            processImports(configurationClass, asSourceClass(configurationClass),
                                    asSourceClasses(entry.getImportClassName()), false);
                        }
                        catch (BeanDefinitionStoreException ex) {
                            throw ex;
                        }
                        catch (Throwable ex) {
                            throw new BeanDefinitionStoreException(
                                    "Failed to process import candidates for configuration class [" +
                                            configurationClass.getMetadata().getClassName() + "]", ex);
                        }
                    });
                }
            }
    grouping.getImports()的实现是
    public Iterable<Group.Entry> getImports() {
                for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
                    this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                            deferredImport.getImportSelector());
                }
                return this.group.selectImports();
            }
    group.process的调用了AutoConfigurationImportSelector.process 方法,这样就和前面的从注解看实现的步骤对上了,加载配置类的入口就找到了,并且经过一层过滤
    public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
                Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                        () -> String.format("Only %s implementations are supported, got %s",
                                AutoConfigurationImportSelector.class.getSimpleName(),
                                deferredImportSelector.getClass().getName()));
    // 这个地方就是执行的导入配置类 AutoConfigurationEntry autoConfigurationEntry
    = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } }

    再回到processGroupImports() 这个方法很复杂,作用是循环调用所有配置类,将自动配置类中的有 @ComponentScan @Import  @ImportResource 这些注解的类都找到,如果没有注解作为配置类使用,并且将结果存在在configClass中,这个类中存放的是所有配置类及配置类中产生Bean方法的信息,后面将通过这些信息确定要最终加载哪些Bean。

    this.reader.loadBeanDefinitions(configClasses);
    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()) {
                registerBeanDefinitionForImportedConfigurationClass(configClass);
            }
    // 将configerClass 转成BeanDefinition并注册到BeanFactory
    for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }

     ConditionEvaluator.shouldSkip 的作用就是根据判断Condition注解能否生效

    public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
            if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
                return false;
            }
    
            if (phase == null) {
                if (metadata instanceof AnnotationMetadata &&
                        ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
                    return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
                }
                return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
            }
    
            List<Condition> conditions = new ArrayList<>();
            for (String[] conditionClasses : getConditionClasses(metadata)) {
                for (String conditionClass : conditionClasses) {
                    Condition condition = getCondition(conditionClass, this.context.getClassLoader());
                    conditions.add(condition);
                }
            }
    
            AnnotationAwareOrderComparator.sort(conditions);
    
            for (Condition condition : conditions) {
                ConfigurationPhase requiredPhase = null;
                if (condition instanceof ConfigurationCondition) {
                    requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
                }
                if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
                    return true;
                }
            }
    
            return false;
        }
    这样整个自动配置的链路就串起来了,加载自动配类,使用条件注解判断哪些Bean需要加载到容器中都可以找到实现。
  • 相关阅读:
    课堂练习-电梯调度
    团队开发项目———来用————用户调研报告
    购书思想课堂作业4.14
    针对《来用》的NABC分析
    《梦断代码》读书笔记3
    《梦断代码》读书笔记2
    《大道至简》阅读笔记2
    《大道至简》阅读笔记1
    课堂练习之找出所有的“1”
    典型用户与场景分析
  • 原文地址:https://www.cnblogs.com/li-zhi-long/p/14422711.html
Copyright © 2011-2022 走看看