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. * <p>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)">;
}
@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);
}
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><? <span style="color: rgba(0, 0, 255, 1)">extends</span> Condition><span style="color: rgba(0, 0, 0, 1)">[] value();
}
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.
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);
}
其中 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 &&<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<Condition> conditions = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList<><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) && !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)">;
}
我们再回过头去看 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) -><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><BeanDefinitionHolder> candidates = <span style="color: rgba(0, 0, 255, 1)">new</span> LinkedHashSet<><span style="color: rgba(0, 0, 0, 1)">(configCandidates); Set</span><ConfigurationClass> alreadyParsed = <span style="color: rgba(0, 0, 255, 1)">new</span> HashSet<><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<ConfigurationClass> configClasses = <span style="color: rgba(0, 0, 255, 1)">new</span> LinkedHashSet<><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() ><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><String> oldCandidateNames = <span style="color: rgba(0, 0, 255, 1)">new</span> HashSet<><span style="color: rgba(0, 0, 0, 1)">(Arrays.asList(candidateNames)); Set</span><String> alreadyParsedClasses = <span style="color: rgba(0, 0, 255, 1)">new</span> HashSet<><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) && !<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> && !<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(); }
}
@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)。