本文灵感源自马士兵MAC课程-源码五班
案例代码 实体类 Boss 和 Bar 1 2 3 4 5 6 7 package com.eitan.condition.entity;import org.springframework.stereotype.Component;@Component public class Boss {}
1 2 3 4 package com.eitan.condition.entity;public class Bar {}
可以看到,Boss 上是添加了 @Component 注解的,Spring 通过包扫描可以将其添加进容器
BarConfiguration 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.eitan.condition.config;import com.eitan.condition.condition.ExistBossCondition;import com.eitan.condition.entity.Bar;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Conditional;import org.springframework.context.annotation.Configuration;@Configuration public class BarConfiguration { @Bean @Conditional(ExistBossCondition.class) public Bar bar () { return new Bar(); } }
Bar 通过配置类将其注入进容器,@Conditional 注解表示必须满足 ExistBossCondition.class 是匹配才会将其注入进容器
ExistBossCondition 1 2 3 4 5 6 7 8 9 10 11 12 13 package com.eitan.condition.condition;import com.eitan.condition.entity.Boss;import org.springframework.context.annotation.Condition;import org.springframework.context.annotation.ConditionContext;import org.springframework.core.type.AnnotatedTypeMetadata;public class ExistBossCondition implements Condition { @Override public boolean matches (ConditionContext context, AnnotatedTypeMetadata metadata) { return context.getBeanFactory().containsBeanDefinition(Boss.class.getSimpleName().toLowerCase()); } }
这里的匹配规则是,如果容器中已经存在了 boss 的定义信息,才会将其所影响的类加载进容器
MyApplicationContext 1 2 3 4 5 6 7 8 9 10 11 12 13 package com.eitan.condition;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import java.util.Arrays;public class MyApplicationContext { public static void main (String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.eitan.condition" ); Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println); } }
允许代码后,可以发现打印出了 bar 和 boss
当除去 Boss.class 上的 @Component 之后,bar 和 boss 都不会被打印
源码分析 Boss 和 BarConfiguration 是怎么加载进容器的 AnnotationConfigApplicationContext的构造方法 1 2 3 4 5 public AnnotationConfigApplicationContext (String... basePackages) { this (); scan(basePackages); refresh(); }
发生在 scan(basePackages) 方法中,最终调用的是 ClassPathBeanDefinitionScanner 的 doSacn 方法
doScan 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 protected Set<BeanDefinitionHolder> doScan (String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified" ); Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this .scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this .beanNameGenerator.generateBeanName(candidate, this .registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this .registry); beanDefinitions.add(definitionHolder); registerBeanDefinition(definitionHolder, this .registry); } } } return beanDefinitions; }
继续跟踪 findCandidateComponents(basePackage); 方法,通过 doScan 方法扫描到的 BeanDefinition 都是 ScannedGenericBeanDefinition
查看扫描到的类 可以看到,Boss 和 BarConfiguration 已经被扫描到了,但是 Bar 没有被扫描到
Bar 的加载时机 发生在 ConfigurationClassPostProcessor 的 postProcessBeanDefinitionRegistry 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Override public void postProcessBeanDefinitionRegistry (BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); if (this .registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this .factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this .registriesPostProcessed.add(registryId); processConfigBeanDefinitions(registry); }
ConfigurationClassPostProcessor 永远是最先被调用的 PostProcessor,其作用是用来处理容器中的配置类
processConfigBeanDefinitions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public void processConfigBeanDefinitions (BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); 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)); } } ...... 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" ); parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); if (this .reader == null ) { this .reader = new ConfigurationClassBeanDefinitionReader( registry, this .sourceExtractor, this .resourceLoader, this .environment, this .importBeanNameGenerator, parser.getImportRegistry()); } this .reader.loadBeanDefinitions(configClasses); ...... }
loadBeanDefinitionsForConfigurationClass 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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); } for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
loadBeanDefinitionsForBeanMethod 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private void loadBeanDefinitionsForBeanMethod (BeanMethod beanMethod) { ConfigurationClass configClass = beanMethod.getConfigurationClass(); MethodMetadata metadata = beanMethod.getMetadata(); String methodName = metadata.getMethodName(); if (this .conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) { configClass.skippedBeanMethods.add(methodName); return ; } if (configClass.skippedBeanMethods.contains(methodName)) { return ; } ....
shouldSkip 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 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 ; }
这里获取了该方法上的 conditionClasses,然后调用了这些 conditionClasses 的 matches 方法,只要有一个 matches 返回 false,则这个方法就返回 true,上层方法就会跳过这个类的加载
总结
doScan 方法会扫描加载 basePackages 下所有注释了 @Component 的类
@Configuration 包含了 @Component
在调用 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor 时最先调用的就是 ConfigurationClassPostProcessor,该类中解析了对配置类的解析。
该类处理了 @Component、@ComponentScan、@Import、@ImportSource、@Bean 注解
在解析配置类或者方法时,会先调用 shouldSkip 方法,该方法中会获取方法或类上修饰的 @Conditional 注解的 conditionClasses
遍历调用这些 conditionClasses 实现了 Condition 接口的 matches 方法,当有一个返回 false 这跳过解析