spring-boot-2.0.3源码篇 - @Configuration、Condition与@Conditional

开心一刻

  一名劫匪慌忙中窜上了一辆车的后座,上车后发现主驾和副驾的一男一女疑惑地回头看着他

  他立即拔出枪威胁到:“赶快开车,甩掉后面的警车,否则老子一枪崩了你!”

  于是副驾上的男人转过脸对那女的说:“大姐,别慌,听我口令把刚才的动作再练习一遍,挂一档,轻松离合,轻踩油门,走... 走,哎 走... 哎,哎,对,走走...

  最后,三人都躺到了医院,劫匪的手上还戴上了铐子...

劫匪的内心

前情回顾

  估摸着大家已经忘记了createApplicationContext的内容,本文不做过多的回顾,只是提醒大家:在 AnnotationConfigServletWebServerApplicationContext 的实例化过程中,实例化了 AnnotatedBeanDefinitionReader,另外也将 ConfigurationClassPostProcessor 定义注册到了 beanFactory 中,如下图所示

  看着 AnnotatedBeanDefinitionReader、ConfigurationClassPostProcessor 是不是隐约感觉到了什么? ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor,BeanDefinitionRegistryPostProcessor 又实现了 BeanFactoryPostProcessor,关于 BeanFactoryPostProcessor,大家可以看看这篇文章:Spring 拓展接口之 BeanFactoryPostProcessor,占位符与敏感信息解密原理

概念介绍与应用

  @Configuration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Explicitly specify the name of the Spring bean definition associated
 * with this Configuration class. If left unspecified (the common case),
 * a bean name will be automatically generated.
 * &lt;p&gt;The custom name applies only if the Configuration class is picked up via
 * component scanning or supplied directly to a {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)"> AnnotationConfigApplicationContext}.
 * If the Configuration class is registered as a traditional XML bean definition,
 * the name/id of the bean element will take precedence.
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span><span style="color: rgba(0, 128, 0, 1)"> the suggested component name, if any (or empty String otherwise)
 * </span><span style="color: rgba(128, 128, 128, 1)">@see</span><span style="color: rgba(0, 128, 0, 1)"> org.springframework.beans.factory.support.DefaultBeanNameGenerator
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
@AliasFor(annotation </span>= Component.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)
String value() </span><span style="color: rgba(0, 0, 255, 1)">default</span> ""<span style="color: rgba(0, 0, 0, 1)">;

}

View Code

    @Configuration 能够修饰 Class、interface 和 enum,用的最多的还是标注在类上,相当于把该类作为 spring 的 xml 配置文件中的 <beans>,用于配置 spring 容器;@Configuration 往往会结合 @Bean 来使用,@Bean 等价于 spring 的 xml 配置文件中的 <bean>,用于注册 bean 对象。@Configuration 和 @Bean 组成了基于 java 类的配置,是 spring 的推荐配置方式。最简单的使用如下

@Configuration
public class MyConfiguration {
@Bean
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Cat mycat() {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Cat();
}

}

    如上代码就会在 spring 容器中注册一个名叫 mycat 的 Cat 类型的 Bean

  Condition

@FunctionalInterface
public interface Condition {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * Determine if the condition matches.
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> context the condition context
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> metadata metadata of the {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)"> org.springframework.core.type.AnnotationMetadata class}
 * or {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)"> org.springframework.core.type.MethodMetadata method} being checked
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span><span style="color: rgba(0, 128, 0, 1)"> {</span><span style="color: rgba(128, 128, 128, 1)">@code</span><span style="color: rgba(0, 128, 0, 1)"> true} if the condition matches and the component can be registered,
 * or {</span><span style="color: rgba(128, 128, 128, 1)">@code</span><span style="color: rgba(0, 128, 0, 1)"> false} to veto the annotated component's registration
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

View Code

    Spring 的条件化配置,当我们向 spring 注册 bean 时,可以对这个 bean 添加一定的自定义条件,当满足这个条件时注册这个 bean,否则不注册。springboot 中部分实现子类如下

    springboot 更多实现请查看 org.springframework.boot.autoconfigure.condition 包。Condition 一般配合 @Conditional 使用,更多信息往下看

  @Conditional

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * All {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)"> Condition}s that must {</span><span style="color: rgba(128, 128, 128, 1)">@linkplain</span><span style="color: rgba(0, 128, 0, 1)"> Condition#matches match}
 * in order for the component to be registered.
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
Class</span>&lt;? <span style="color: rgba(0, 0, 255, 1)">extends</span> Condition&gt;<span style="color: rgba(0, 0, 0, 1)">[] value();

}

View Code

    Spring 的条件注解,其 value 是一个 Class<? extends Condition>[],只有数组中的全部 Condition 全部匹配成功时,被 @Conditional 修饰的组件才会被注册到 Spring 容器中。@Conditional 只是一个标志,标示需要进行条件判断,而具体的判断规则则由具体的 Condition 来实现。

    在 SpringBoot 源码中很容易看到被 @Conditional 注解的组合注解,例如:@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnClass、@ConditionalOnMissingClass 等,具体如下

    springboot 还提供了 AutoConfigureAfter、AutoConfigureBefore、AutoConfigureOrder,看名字基本知道其作用,具体细节需要大家自己去跟了。

  完整应用案例

    接口都能访问通,数据返回也都正确,非常完美

    完整工程代码:spring-boot-condition

    当我们把 MyConfiguration 中的 myCat 方法注释掉(ConditionWeb 中的 cat 相关也注释掉),再启动应用的时候,应用报错启动不起来,提示如下信息:

Description:

Field dog in com.lee.condition.web.ConditionWeb required a bean of type 'com.lee.condition.model.Dog' that could not be found.
- Bean method 'myDog' in 'MyConfiguration' not loaded because @ConditionalOnBean (types: com.lee.condition.model.Cat; SearchStrategy: all) did not find any beans of type com.lee.condition.model.Cat

Action:

Consider revisiting the conditions above or defining a bean of type 'com.lee.condition.model.Dog' in your configuration.

View Code

    ConditionWeb 中需要 Dog 类型的 bean,而 Dog 实例化又依赖 Cat 实例,而我们没有实例化 Cat,所以应用启动报错,提示如上信息 

源码探究

  我们要探究什么了?不探究太细,就探究 @Configuration 修饰的配置类是何时解析的,@Conditional 是何时生效、如何生效的

  @Configuration 修饰的配置类是何时解析的

    ConfigurationClassPostProcessor 是一个 BeanFactoryPostProcessor(可以查看 ConfigurationClassPostProcessor 的类继承结构图),那么我们从 AbstractApplicationContext 的 refresh 方法调用的 invokeBeanFactoryPostProcessors(beanFactory) 方法开始

    来到了 processConfigurationClass 方法,其详细代码如下

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    // ConfigurationClass 是否应该被 skip
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }
ConfigurationClass existingClass </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.configurationClasses.get(configClass);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (existingClass != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (configClass.isImported()) {
        </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (existingClass.isImported()) {
            existingClass.mergeImportedBy(configClass);
        }
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Otherwise ignore new imported config class; existing non-imported class overrides it.</span>
        <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
    }
    </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Explicit bean definition found, probably replacing an import.
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Let's remove the old one and go with the new one.</span>
        <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.configurationClasses.remove(configClass);
        </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.knownSuperclasses.values().removeIf(configClass::equals);
    }
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Recursively process the configuration class and its superclass hierarchy. 递归处理configuration class和它的父级类
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 也就说会递归处理我们的应用入口类:ConditionApplication.class,以及ConditionApplication.class的父级类</span>
SourceClass sourceClass =<span style="color: rgba(0, 0, 0, 1)"> asSourceClass(configClass);
</span><span style="color: rgba(0, 0, 255, 1)">do</span><span style="color: rgba(0, 0, 0, 1)"> {
    sourceClass </span>=<span style="color: rgba(0, 0, 0, 1)"> doProcessConfigurationClass(configClass, sourceClass);
}
</span><span style="color: rgba(0, 0, 255, 1)">while</span> (sourceClass != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将满足条件的ConfigurationClass都放入configurationClasses集合中
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 后续会加载configurationClasses集合中所有的ConfigurationClass中配置的bean定义</span>
<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.configurationClasses.put(configClass, configClass);

}

View Code

    其中 shouldSkip 方法如下

/**
 * Determine if an item should be skipped based on {@code @Conditional} annotations.
 * @param metadata the meta data
 * @param phase the phase of the call
 * @return if the item should be skipped
 */
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
    // 如果这个类没有注解修饰,或者没有被 @Conditional 注解(包括 Conditional 系列)所修饰,不会 skip
    if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
        return false;
    }
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果参数中沒有设置条件注解的生效阶段</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (phase == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (metadata <span style="color: rgba(0, 0, 255, 1)">instanceof</span> AnnotationMetadata &amp;&amp;<span style="color: rgba(0, 0, 0, 1)">
            ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 要解析的配置类的条件集合,即@Conditional的value</span>
List&lt;Condition&gt; conditions = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (String[] conditionClasses : getConditionClasses(metadata)) {
    </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (String conditionClass : conditionClasses) {
        Condition condition </span>= getCondition(conditionClass, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.context.getClassLoader());
        conditions.add(condition);
    }
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 对条件进行排序</span>

AnnotationAwareOrderComparator.sort(conditions);

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 遍历条件,逐个匹配</span>
<span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (Condition condition : conditions) {
    ConfigurationPhase requiredPhase </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (condition <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> ConfigurationCondition) {
        requiredPhase </span>=<span style="color: rgba(0, 0, 0, 1)"> ((ConfigurationCondition) condition).getConfigurationPhase();
    }
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 条件注解的生效阶段满足,一旦有条件匹配不成功,则返回true,skip此类</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span> ((requiredPhase == <span style="color: rgba(0, 0, 255, 1)">null</span> || requiredPhase == phase) &amp;&amp; !condition.matches(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.context, metadata)) {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}

</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;

}

View Code

    我们再回过头去看 processConfigBeanDefinitions 方法

/**
 * Build and validate a configuration model based on the registry of
 * {@link Configuration} classes.
 *  验证 @Configuration 修饰的类,满足条件则构建成 configuration model
 */
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();
</span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (String beanName : candidateNames) {
    BeanDefinition beanDef </span>=<span style="color: rgba(0, 0, 0, 1)"> registry.getBeanDefinition(beanName);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||<span style="color: rgba(0, 0, 0, 1)">
            ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
        </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (logger.isDebugEnabled()) {
            logger.debug(</span>"Bean definition has already been processed as a configuration class: " +<span style="color: rgba(0, 0, 0, 1)"> beanDef);
        }
    }
    </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.metadataReaderFactory)) {
        configCandidates.add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> BeanDefinitionHolder(beanDef, beanName));
    }
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Return immediately if no @Configuration classes were found</span>
<span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (configCandidates.isEmpty()) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Sort by previously determined @Order value, if applicable</span>
configCandidates.sort((bd1, bd2) -&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">int</span> i1 =<span style="color: rgba(0, 0, 0, 1)"> ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
    </span><span style="color: rgba(0, 0, 255, 1)">int</span> i2 =<span style="color: rgba(0, 0, 0, 1)"> ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> Integer.compare(i1, i2);
});

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Detect any custom bean name generation strategy supplied through the enclosing application context
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 检测自定义的bean生成策略</span>
SingletonBeanRegistry sbr = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (registry <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> SingletonBeanRegistry) {
    sbr </span>=<span style="color: rgba(0, 0, 0, 1)"> (SingletonBeanRegistry) registry;
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.localBeanNameGeneratorSet) {
        BeanNameGenerator generator </span>=<span style="color: rgba(0, 0, 0, 1)"> (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
        </span><span style="color: rgba(0, 0, 255, 1)">if</span> (generator != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.componentScanBeanNameGenerator =<span style="color: rgba(0, 0, 0, 1)"> generator;
            </span><span style="color: rgba(0, 0, 255, 1)">this</span>.importBeanNameGenerator =<span style="color: rgba(0, 0, 0, 1)"> generator;
        }
    }
}

</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.environment == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.environment = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> StandardEnvironment();
}

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Parse each @Configuration class
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 解析每一个被@Configuration修饰的class</span>
ConfigurationClassParser parser = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ConfigurationClassParser(
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.metadataReaderFactory, <span style="color: rgba(0, 0, 255, 1)">this</span>.problemReporter, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.environment,
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.resourceLoader, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.componentScanBeanNameGenerator, registry);

Set</span>&lt;BeanDefinitionHolder&gt; candidates = <span style="color: rgba(0, 0, 255, 1)">new</span> LinkedHashSet&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">(configCandidates);
Set</span>&lt;ConfigurationClass&gt; alreadyParsed = <span style="color: rgba(0, 0, 255, 1)">new</span> HashSet&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">(configCandidates.size());
</span><span style="color: rgba(0, 0, 255, 1)">do</span><span style="color: rgba(0, 0, 0, 1)"> {
    parser.parse(candidates);    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 解析过程中会将满足条件的@Configuration class存放到configurationClasses中</span>

parser.validate();

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 满足条件的@Configuration class 都存放在了parser的configurationClasses中</span>
    Set&lt;ConfigurationClass&gt; configClasses = <span style="color: rgba(0, 0, 255, 1)">new</span> LinkedHashSet&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">(parser.getConfigurationClasses());
    configClasses.removeAll(alreadyParsed);

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Read the model and create bean definitions based on its content
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 读取@Configuration class中的配置(各个@Bean),并创建对应的bean definition(后续创建bean实例会用到bean定义)</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.reader == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
        </span><span style="color: rgba(0, 0, 255, 1)">this</span>.reader = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ConfigurationClassBeanDefinitionReader(
                registry, </span><span style="color: rgba(0, 0, 255, 1)">this</span>.sourceExtractor, <span style="color: rgba(0, 0, 255, 1)">this</span>.resourceLoader, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.environment,
                </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.importBeanNameGenerator, parser.getImportRegistry());
    }
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.reader.loadBeanDefinitions(configClasses);    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 加载全部@Configuration class中的配置</span>

alreadyParsed.addAll(configClasses);

    candidates.clear();
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (registry.getBeanDefinitionCount() &gt;<span style="color: rgba(0, 0, 0, 1)"> candidateNames.length) {
        String[] newCandidateNames </span>=<span style="color: rgba(0, 0, 0, 1)"> registry.getBeanDefinitionNames();
        Set</span>&lt;String&gt; oldCandidateNames = <span style="color: rgba(0, 0, 255, 1)">new</span> HashSet&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">(Arrays.asList(candidateNames));
        Set</span>&lt;String&gt; alreadyParsedClasses = <span style="color: rgba(0, 0, 255, 1)">new</span> HashSet&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
        </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (ConfigurationClass configurationClass : alreadyParsed) {
            alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
        }
        </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (String candidateName : newCandidateNames) {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">oldCandidateNames.contains(candidateName)) {
                BeanDefinition bd </span>=<span style="color: rgba(0, 0, 0, 1)"> registry.getBeanDefinition(candidateName);
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, <span style="color: rgba(0, 0, 255, 1)">this</span>.metadataReaderFactory) &amp;&amp;
                        !<span style="color: rgba(0, 0, 0, 1)">alreadyParsedClasses.contains(bd.getBeanClassName())) {
                    candidates.add(</span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> BeanDefinitionHolder(bd, candidateName));
                }
            }
        }
        candidateNames </span>=<span style="color: rgba(0, 0, 0, 1)"> newCandidateNames;
    }
}
</span><span style="color: rgba(0, 0, 255, 1)">while</span> (!<span style="color: rgba(0, 0, 0, 1)">candidates.isEmpty());

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (sbr != <span style="color: rgba(0, 0, 255, 1)">null</span> &amp;&amp; !<span style="color: rgba(0, 0, 0, 1)">sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
    sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}

</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.metadataReaderFactory <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> CachingMetadataReaderFactory) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Clear cache in externally provided MetadataReaderFactory; this is a no-op
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> for a shared cache since it'll be cleared by the ApplicationContext.</span>
    ((CachingMetadataReaderFactory) <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.metadataReaderFactory).clearCache();
}

}

View Code

  @Conditional 是何时生效、如何生效的

    这个问题再上面已经全部得到体现,Spring 不会无脑的加载所有的 @Configuration class,只会加载满足条件的 @Configuration class,而 @Conditional 就是条件标志,至于条件匹配规则这由 Condition 提供;shouldSkip 方法中用到 Conditional 和 Condition,完成条件的匹配处理。

总结

  1、@Configuration 和 @Bean 组成了基于 java 类的配置,与 xml 中的 <Beans>、<Bean> 功能一致,Spring 推荐 java 类的配置;

  2、Condition 与 @Conditional 实现了条件配置,只有满足了条件的 @Configuration class 和 @Bean 才会被注册到 Spring 容器;

  3、Spring 以我们的应用启动类为基础来递归扫描配置类,包括我们应用中的配置类、Spring 自己的以及第三方的配置类(springboot 集成的各种配置类 (spring-boot-autoconfigure-xxx.RELEASE.jar 下的 spring.factories 文件中的 Auto Configure),还有 pageHelper 的自动配置,等等);前提是需要开启自动配置(@EnableAutoConfiguration)。

参考

  SpringBoot 源码分析之条件注解的底层实现

  Spring 工具类 ConfigurationClassParser 分析得得到配置类