spring boot配置类注册深入解析
Posted ac_dao_di
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring boot配置类注册深入解析相关的知识,希望对你有一定的参考价值。
前言
spring ApplicationContext的刷新总体来看有两个过程,第一个是注册BeanDefinition,提供整个IOC容器初始化的材料;第二个是根据BeanDefinition加载bean。从spring boot开始,推荐使用的是注解驱动编程,借助大量的自动化配置类注入BeanDefinition,之前Spring使用XML方式注入BeanDefinition的基本废弃,因此了解自动化配置类如何注入BeanDefinition,条件注解如何生效,重要性不言而喻。本文将针对自动化配置起重要作用的ConfigurationClassPostProcessor进行深入解析,通过本文可以解答如下问题:
- 自动化配置类生效的机制,条件注解的处理过程
- spring boot自动化配置类,有的条件类不存在,工程里打开是标红的,spring boot解析时为什么不报错,又是如何通过编译的?
- @EnableXXXX到底是什么原理?注册BeanDefinition有哪些高端的姿势?
- @Configuration和@Component到底有什么区别。
本文源码见:源码
一张图总结整个配置类注册过程:
1. ConfigurationClassPostProcessor
ConfigurationClassPostProcessor,实现PriorityOrdered,主要有两个作用:
- 在postProcessBeanDefinitionRegistry注入beanDefinition。
- 在postProcessBeanFactory,替换@Configuration class注解的beanClass为cglib增强。同时注入一个BeanPostProcessor:ImportAwareBeanPostProcessor
核心逻辑如下:
- 先从当前BeanFactory找到所有含有@Configuration或者标有@Component/@ComponentScan/@Import/@ImportResource注解,或者有@Bean方法,作为初始化的配置类
- ConfigurationPhase.PARSE_CONFIGURATION阶段:使用ConfigurationClassParser,解析成所有的ConfigurationClass,包括其上的@import,以及@Bean method,全部解析并存到ConfigurationClass中。这个阶段一般不会load class。
- ConfigurationPhase.REGISTER_BEAN阶段:再把ConfigurationClass中的BeanMethod啥的注册到BeanDefinitionRegistry中,如@Import(ImportRegistrar),则直接register。这个阶段可能会load class,同时对于@Bean方法上的注解,会继续处理。
- 获取到新注册的未处理的bean definition,继续循环处理下一批新增的配置类。
// ConfigurationClassPostProcessor
@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);
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry)
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
// 先从当前factory找到所有含有@Configuration或者该bean标有@Component/@ComponentScan/@Import/@ImportResource注解,或者有@Bean方法。
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));
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty())
return;
// Sort by previously determined @Order value, if applicable
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();
// Parse each @Configuration class
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");
// 1、使用ConfigurationClassParser,解析成所有的ConfigurationClass,包括其上的@import,以及@Bean method,全部解析并存到ConfigurationClass中
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());
// 2、再把ConfigurationClass中的BeanMethod啥的注册到BeanDefinitionRegistry中,如@Import(ImportRegistrar),则直接register
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length)
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed)
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
for (String candidateName : newCandidateNames)
if (!oldCandidateNames.contains(candidateName))
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName()))
// 3、获取到新注册的bean definition,继续循环处理
candidates.add(new BeanDefinitionHolder(bd, candidateName));
candidateNames = newCandidateNames;
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME))
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory)
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
首先介绍下,一个配置类什么时候是full模式,什么时候是lite模式:
- full模式:配置类标记有@Configuration且proxyBeanMethos=true/null,则是大模式
- lite模式,有两种情况:
- 配置类标记有@Configuration且proxyBeanMethos=false
- 配置类标记有@Component/@ComponentScan/@Import/@ImportResource注解,或者有@Bean方法,则是小的配置类。
只有上面两个模式的配置类,才会进一步处理,才会调用processConfigurationClass,包括内部类、ComponentScan扫描到的类、这一批新增加的类
// ConfigurationClassUtils
private static final Set<String> candidateIndicators = new HashSet<>(8);
static
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory)
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null)
return false;
AnnotationMetadata metadata;
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName()))
// Can reuse the pre-parsed metadata from the given BeanDefinition...
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass())
// Check already loaded Class if present...
// since we possibly can't even load the class file for this Class.
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
BeanPostProcessor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
EventListenerFactory.class.isAssignableFrom(beanClass))
return false;
metadata = AnnotationMetadata.introspect(beanClass);
else
try
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
catch (IOException ex)
if (logger.isDebugEnabled())
logger.debug("Could not find class file for introspecting configuration annotations: " +
className, ex);
return false;
// 标记有@Configuration且proxyBeanMethos=true/null,则是大模式
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods")))
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
else if (config != null || isConfigurationCandidate(metadata))
// 标记有@Configuration且proxyBeanMethos=false,则是lite模式
// 该类标有@Component/@ComponentScan/@Import/@ImportResource注解,或者有@Bean方法,则是小的配置类。
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
else
return false;
// It's a full or lite configuration candidate... Let's determine the order value, if any.
Integer order = getOrder(metadata);
if (order != null)
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
return true;
public static boolean isConfigurationCandidate(AnnotationMetadata metadata)
// Do not consider an interface or an annotation...
if (metadata.isInterface())
return false;
// Any of the typical annotations found?
for (String indicator : candidateIndicators)
if (metadata.isAnnotated(indicator))
return true;
// Finally, let's look for @Bean methods...
return hasBeanMethods(metadata);
2. ConfigurationClassParser.parse
启动配置类只有DemoApplication,由于启动类已经加载为class了,这里AnnotationMetadata为StandardAnnotationMetadata,底层基于反射获取到注解等类信息;如果未加载class,则一般使用ASM读取类文件,避免过早加载类,实例为SimpleAnnotationMetadata,底层使用SimpleMetadataReader读取。
每一批类处理完成后,才调用deferredImportSelectorHandler.process方法,保证@Import defferred Import类最后处理。
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();
// StandardAnnotationMetadata
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
protected final void parse(@Nullable String className, String beanName) throws IOException
Assert.notNull(className, "No bean class name for configuration class bean definition");
// 利用metadataRederFactory读取,就是之前的ConcurrentReferenceCachingMetadataReaderFactory,最终得到的reader是SimpleMetadataReader
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
ConfigurationClass(MetadataReader metadataReader, String beanName)
Assert.notNull(beanName, "Bean name must not be null");
// 读取metadata
this.metadata = metadataReader.getAnnotationMetadata();
this.resource = metadataReader.getResource();
this.beanName = beanName;
最后,每个配置类,都会进入到processConfigurationClass这个方法进行处理,首先是看该配置类是否满足条件,不满足则不处理,否则递归处理,从当前类往上找到所有父类,不断处理。
在这过程中配置类是相同的,也就是说配置类ConfigurationClass会一直传递,用于存储获取的BeanMethod/import类等,但是sourceClass会变化,依次为当前类,父类,父父类…
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION))
return;
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null)
if (configClass.isImported())
if (existingClass.isImported())
existingClass.mergeImportedBy(configClass);
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
else
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
/**
* Apply processing and build a complete @link ConfigurationClass by reading the
* annotations, members and methods from the source class. This method can be called
* multiple times as relevant sources are discovered.
* @param configClass the configuration class being build
* @param sourceClass a source class
* @return the superclass, or @code null if none found or previously processed
*/
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException
if (configClass.getMetadata().isAnnotated(Component.class.getName()))
// 1、在扫描一个java config类之前,第一步先处理内部类并注册,注意内部类必须标记有@Component或者有@Bean method,然后再处理@Bean方法
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
// Process any @PropertySource annotations
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");
// Process any @ComponentScan annotations
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)
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions)
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null)
bdCand = holder.getBeanDefinition();
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory))
parse(bdCand.getBeanClassName(), holder.getBeanName());
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// Process any @ImportResource annotations
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);
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods)
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass())
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass))
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
// No superclass -> processing is complete
return null;
3. 处理sourceClass的内部类
如果配置类标记有@Component,对于每个sourceClass,则会处理sourceClass符合条件的内部类,也就是符合isConfigurationCandidate,标有@Configuration/@Component/@ComponentScan/@Import/@ImportResource注解,或者有@Bean方法,才会进行处理。可以看到调用的依旧是processConfigurationClass,构成递归,底层对每个内部类,依旧会进行条件判断。
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
Predicate<String> filter) throws IOException
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty())
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses)
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName()))
candidates.add(memberClass);
OrderComparator.sort(candidates);
for (SourceClass candidate : candidates)
if (this.importStack.contains(configClass))
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
else
this.importStack.push(configClass);
try
processConfigurationClass(candidate.asConfigClass(configClass), filter);
finally
this.importStack.pop();
4. 处理sourceClass上的注解PropertySource
之后处理sourceClass上的注解PropertySource,并添加到environment
private void processPropertySource(AnnotationAttributes propertySource) throws IOException
String name = propertySource.getString("name");
String[] locations = propertySource.getStringArray("value");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
for (String location : locations)
try
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
Resource resource = this.resourceLoader.getResource(resolvedLocation);
ResourcePropertySource rps = (StringUtils.hasText(name) ?
new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
addPropertySource(rps);
catch (IllegalArgumentException ex)
// from resolveRequiredPlaceholders
if (!ignoreResourceNotFound)
throw ex;
catch (FileNotFoundException ex)
// from ResourcePropertySource constructor
if (!ignoreResourceNotFound)
throw ex;
5. 处理sourceClass的注解ComponentScan
之后处理sourceClass的注解ComponentScan,默认不指定包的话,是ComponentScan注解声明类所在的包。而且扫描通过的话,会直接注册该类对应的bean definition到BeanDefinitionRegistry中,其上的条件注解也会生效,也可以使用includeFilter/excludeFilter来进一步过滤。底层使用的是ClassPathBeanDefinitionScanner#doScan方法。如果使用findCandidateComponents,则不会有注册功能,仅仅是扫描而已。如果扫描到的类标有@Configuration/@Component/@ComponentScan/@Import/@ImportResource注解,或者有@Bean方法,会继续进行processConfigurationClass处理。最终注册为ScannedGenericBeanDefinition。
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)
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions)
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null)
bdCand = holder.getBeanDefinition();
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory))
parse(bdCand.getBeanClassName(), holder.getBeanName());
在本实例中,DemoApplication上标记有@ComponentScan,所以会进行扫描。
且由于没有配置扫描的包路径,默认为声明类所在的包:
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)
// 从类路径找到符合条件的bean definition
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);
// 注册符合条件的bean definition到registry
registerBeanDefinition(definitionHolder, this.registry);
return beanDefinitions;
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException
if (!this.registry.containsBeanDefinition(beanName))
return true;
BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
if (originatingDef != null)
existingDef = originatingDef;
// 如果扫描到的bean已经注册了,beanName一致,则可能会报错
if (isCompatible(beanDefinition, existingDef))
return false;
throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
public Set<BeanDefinition> findCandidateComponents(String basePackage)
if (this.componentsIndex != null && indexSupportsIncludeFilters())
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
else
return scanCandidateComponents(basePackage);
private Set<BeanDefinition> scanCandidateComponents(String basePackage)
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try
// 类文件全部解析为resource
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources)
if (traceEnabled)
logger.trace("Scanning " + resource);
try
// 使用ASM读取,解析类文件上的注解
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
if (isCandidateComponent(metadataReader))
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
// 符合条件的resource,会构建为ScannedGenericBeanDefinition
if (isCandidateComponent(sbd))
if (debugEnabled)
logger.debug("Identified candidate component class: " + resource);
candidates.add(sbd);
else
if (debugEnabled)
logger.debug("Ignored because not a concrete top-level class: " + resource);
else
if (traceEnabled)
logger.trace("Ignored because not matching any filter: " + resource);
catch (FileNotFoundException ex)
if (traceEnabled)
logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());
catch (Throwable ex)
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
catch (IOException ex)
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
return candidates;
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException
for (TypeFilter tf : this.excludeFilters)
if (tf.match(metadataReader, getMetadataReaderFactory()))
return false;
for (TypeFilter tf : this.includeFilters)
if (tf.match(metadataReader, getMetadataReaderFactory()))
// 是否符合环境变量等条件,@Conditional
return isConditionMatch(metadataReader);
return false;
最后,扫描得到两个candiate bean definition:HelloController和UserService
6. 处理sourceClass的注解Import
之后处理sourceClass的注解@Import,收集注解上的class,得到importCandidates。import的class有三种:
- DeferredImportSelector,实例化后交给deferredImportSelectorHandler.handle处理,主要是分组进行聚合,然后排序,保证添加到beanDefinitionRegistry时是有序的,尤其是在@ConditionalOnMissingBean时,要覆盖,很有用。
- ImportSelector,实例化selectImport后,继续调用processImports,一般最后添加的也是普通配置类
- ImportBeanDefinitionRegistrar,实例话后,添加到configClass中,待会进行registerBean。
- 普通配置类,继续调用processConfigurationClass处理。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports)
if (importCandidates.isEmpty())
return;
if (checkForCircularImports && isChainedImportOnStack(configClass))
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
else
this.importStack.push(configClass);
try
for (SourceClass candidate : importCandidates)
if (candidate.isAssignable(ImportSelector.class))
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null)
exclusionFilter = exclusionFilter.or(selectorFilter);
if (selector instanceof DeferredImportSelector)
// 交给deferredImportSelectorHandler处理
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
else
// 找到slelector方法里所有导入的类,继续调用processImports
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class))
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
else
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 普通配置类
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
catch (BeanDefinitionStoreException ex)
throw ex;
catch (Throwable ex)
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
finally
this.importStack.pop();
6.1 自动化配置类过滤和排序
自动化配置类的导入,是通过EnableAutoConfiguration注解导入的,默认会有两个:
- org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar,主要是注册当前EnableAutoConfiguration的basePackage,对应的bean为BasePackages,beanName为AutoConfigurationPackages.class.getName()
- 自动化配置使用的是AutoConfigurationImportSelector,实现了DeferredImportSelector,这里只是暂存到handler中,等这一批处理完,DeferredImportSelector list才会进行统一处理注入。
private class DeferredImportSelectorHandler
@Nullable
private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();
/**
* Handle the specified @link DeferredImportSelector. If deferred import
* selectors are being collected, this registers this instance to the list. If
* they are being processed, the @link DeferredImportSelector is also processed
* immediately according to its @link DeferredImportSelector.Group.
* @param configClass the source configuration class
* @param importSelector the selector to handle
*/
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector)
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
if (this.deferredImportSelectors == null)
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
else
// 添加到list,暂存起来
this.deferredImportSelectors.add(holder);
public void process()
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try
if (deferredImports != null)
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
// 对所有deferred import selector排序,分类注册,最后分组处理
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
handler.processGroupImports();
finally
this.deferredImportSelectors = new ArrayList<>();
处理defer selector import,逐个注入到group handler中,最后再统一selectimport
private class DeferredImportSelectorGroupingHandler
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
public void register(DeferredImportSelectorHolder deferredImport)
// 对于自动导入类,默认是AutoConfigurationGroup class,相同group class会放到一起
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
public void processGroupImports()
for (DeferredImportSelectorGrouping grouping : this.groupings.values())
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
// 对于每个分组,逐个selectImport,最后调用processImports()
grouping.getImports().forEach(entry ->
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
catch (BeanDefinitionStoreException ex)
throw ex;
catch (Throwable ex)
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
);
private Group createGroup(@Nullable Class<? extends Group> type)
Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class);
return ParserStrategyUtils.instantiateClass(effectiveType, Group.class,
ConfigurationClassParser.this.environment,
ConfigurationClassParser.this.resourceLoader,
ConfigurationClassParser.this.registry);
private static class DeferredImportSelectorGrouping
private final DeferredImportSelector.Group group;
private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
DeferredImportSelectorGrouping(Group group)
this.group = group;
public void add(DeferredImportSelectorHolder deferredImport)
this.deferredImports.add(deferredImport);
/**
* Return the imports defined by the group.
* @return each import with its associated configuration class
*/
public Iterable<Group.Entry> getImports()
for (DeferredImportSelectorHolder deferredImport : this.deferredImports)
// 对于每个group实现类及其添加的defer import selector,对自动配置类则是AutoConfigurationGroup,先用group处理下,最后再select imports
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
// 最后再select imports
return this.group.selectImports();
public Predicate<String> getCandidateFilter()
Predicate<String> mergedFilter = DEFAULT_EXCLUSION_FILTER;
for (DeferredImportSelectorHolder deferredImport : this.deferredImports)
Predicate<String> selectorFilter = deferredImport.getImportSelector().getExclusionFilter();
if (selectorFilter != null)
mergedFilter = mergedFilter.or(selectorFilter);
return mergedFilter;
group.process主要是调用AutoConfigurationImportSelector获取到所有的自动化配置类,并暂存到autoConfigurationEntries中。在获取自动化配置类的过程中,会使用META-INF/spring.factories中的AutoConfigurationImportFilter对自动化配置类进行提前过滤,目前主要有三个:
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=
org.springframework.boot.autoconfigure.condition.OnBeanCondition,
org.springframework.boot.autoconfigure.condition.OnClassCondition,
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
private static class AutoConfigurationGroup
implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();
private ClassLoader beanClassLoader;
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
private AutoConfigurationMetadata autoConfigurationMetadata;
@Override
public void setBeanClassLoader(ClassLoader classLoader)
this.beanClassLoader = classLoader;
@Override
public void setBeanFactory(BeanFactory beanFactory)
this.beanFactory = beanFactory;
@Override
public void setResourceLoader(ResourceLoader resourceLoader)
this.resourceLoader = resourceLoader;
@Override
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(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations())
this.entries.putIfAbsent(importClassName, annotationMetadata);
@Override
public Iterable<Entry> selectImports()
if (this.autoConfigurationEntries.isEmpty())
return Collections.emptyList();
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
private AutoConfigurationMetadata getAutoConfigurationMetadata()
if (this.autoConfigurationMetadata == null)
this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
return this.autoConfigurationMetadata;
private List<String> sortAutoConfigurations(Set<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata)
return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata)
.getInPriorityOrder(configurations);
private MetadataReaderFactory getMetadataReaderFactory()
try
return this.beanFactory.getBean(SharedMetadataReaderFactoryContextInitializer.BEAN_NAME,
MetadataReaderFactory.class);
catch (NoSuchBeanDefinitionException ex)
return new CachingMetadataReaderFactory(this.resourceLoader);
提前过滤主要是利用META-INF/spring-autoconfigure-metadata.properties中的配置元数据,只能对class不存在时,导致的条件不生效进行过滤,例如对于conditionalOnBean可以提前过滤掉一部分。
abstract class FilteringSpringBootCondition extends SpringBootCondition
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware
private BeanFactory beanFactory;
private ClassLoader beanClassLoader;
// 实现AutoConfigurationImportFilter,提前过滤掉部分不生效的自动化配置类。
@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata)
ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++)
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null)
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null)
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
return match;
protected abstract ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata);
过滤掉后,只能77个,原先有200多个。
0 = "org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration"
1 = "org.springframework.boot.autoconfigure.aop.AopAutoConfiguration"
2 = "org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration"
3 = "org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration"
4 = "org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration"
5 = "org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration"
6 = "org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration"
7 = "org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration"
8 = "org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration"
9 = "org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration"
10 = "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration"
11 = "org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration"
12 = "org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration"
13 = "org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration"
14 = "org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration"
15 = "org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration"
16 = "org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration"
17 = "org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration"
18 = "org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration"
19 = "org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration"
20 = "org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration"
21 = "org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration"
22 = "org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration"
23 = "org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration"
24 = "org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration"
25 = "org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration"
26 = "org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration"
27 = "org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration"
28 = "org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration"
29 = "org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration"
30 = "org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration"
31 = "org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration"
32 = "org.springframework.boot.actuate.autoconfigure.availability.AvailabilityHealthContributorAutoConfiguration"
33 = "org.springframework.boot.actuate.autoconfigure.availability.AvailabilityProbesAutoConfiguration"
34 = "org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration"
35 = "org.springframework.boot.actuate.autoconfigure.cache.CachesEndpointAutoConfiguration"
36 = "org.springframework.boot.actuate.autoconfigure.cloudfoundry.servlet.CloudFoundryActuatorAutoConfiguration"
37 = "org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpointAutoConfiguration"
38 = "org.springframework.boot.actuate.autoconfigure.context.properties.ConfigurationPropertiesReportEndpointAutoConfiguration"
39 = "org.springframework.boot.actuate.autoconfigure.context.ShutdownEndpointAutoConfiguration"
40 = "org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration"
41 = "org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration"
42 = "org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration"
43 = "org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAutoConfiguration"
44 = "org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration"
45 = "org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration"
46 = "org.springframework.boot.actuate.autoconfigure.info.InfoContributorAutoConfiguration"
47 = "org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration"
48 = "org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration"
49 = "org.springframework.boot.actuate.autoconfigure.logging.LogFileWebEndpointAutoConfiguration"
50 = "org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration"
51 = "org.springframework.boot.actuate.autoconfigure.management.HeapDumpWebEndpointAutoConfiguration"
52 = "org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration"
53 = "org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration"
54 = "org.springframework.boot.actuate.autoconfigure.metrics.JvmMetricsAutoConfiguration"
55 = "org.springframework.boot.actuate.autoconfigure.metrics.LogbackMetricsAutoConfiguration"
56 = "org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration"
57 = "org.springframework.boot.actuate.autoconfigure.metrics.MetricsEndpointAutoConfiguration"
58 = "org.springframework.boot.actuate.autoconfigure.metrics.SystemMetricsAutoConfiguration"
59 = "org.springframework.boot.actuate.autoconfigure.metrics.cache.CacheMetricsAutoConfiguration"
60 = "org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration"
61 = "org.springframework.boot.actuate.autoconfigure.metrics.integration.IntegrationMetricsAutoConfiguration"
62 = "org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration"
63 = "org.springframework.boot.actuate.autoconfigure.metrics.startup.StartupTimeMetricsListenerAutoConfiguration"
64 = "org.springframework.boot.actuate.autoconfigure.metrics.task.TaskExecutorMetricsAutoConfiguration"
65 = "org.springframework.boot.actuate.autoconfigure.metrics.web.client.HttpClientMetricsAutoConfiguration"
66 = "org.springframework.boot.actuate.autoconfigure.metrics.web.servlet.WebMvcMetricsAutoConfiguration"
67 = "org.springframework.boot.actuate.autoconfigure.metrics.web.tomcat.TomcatMetricsAutoConfiguration"
68 = "org.springframework.boot.actuate.autoconfigure.scheduling.ScheduledTasksEndpointAutoConfiguration"
69 = "org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration"
70 = "org.springframework.boot.actuate.autoconfigure.startup.StartupEndpointAutoConfiguration"
71 = "org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration"
72 = "org.springframework.boot.actuate.autoconfigure.trace.http.HttpTraceAutoConfiguration"
73 = "org.springframework.boot.actuate.autoconfigure.trace.http.HttpTraceEndpointAutoConfiguration"
74 = "org.springframework.boot.actuate.autoconfigure.web.mappings.MappingsEndpointAutoConfiguration"
75 = "org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration"
76 = "org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration"
77 = "org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration"
最后调用group.selectImports(),进行排序,主要是根据自动化配置类上的AutoConfigureBefore,AutoConfigureAfter,对顺序进行调整
private static class AutoConfigurationGroup
implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware
private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();
private ClassLoader beanClassLoader;
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
private AutoConfigurationMetadata autoConfigurationMetadata;
@Override
public Iterable<Entry> selectImports()
if (this.autoConfigurationEntries.isEmpty())
return Collections.emptyList();
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
// 排序
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
private List<String> sortAutoConfigurations(Set<String> configurations,
AutoConfigurationMetadata autoConfigurationMetadata)
return new AutoConfigurationSorter(getMetadataReaderFactory(), autoConfigurationMetadata)
.getInPriorityOrder(configurations);
排好序后,每个配置类按照processImports()进一步处理,如果是普通的自动化配置类,会走processConfigurationClass,则需要其上的条件满足加到configurationClasses,才可通过importBy注册到bean definition中。据此,自动化配置导入了大量的配置bean。配置类的条件也会进行过滤。
6.2 ConditionalOnClass实现机制
这里以WebSocketServletAutoConfiguration这个自动化配置类的处理为例。满足条件后,会先处理其内部类。假设处理到JettyWebSocketConfiguration。
@ConditionalOnClass使用常量还是class都没有问题,编译时依赖使用的是optional,能通过编译,但是不会引入依赖。底层bean条件注册使用的是ASM扫描字节码实现的,并不会加载class。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass( Servlet.class, ServerContainer.class )
@ConditionalOnWebApplication(type = Type.SERVLET)
@AutoConfigureBefore(ServletWebServerFactoryAutoConfiguration.class)
public class WebSocketServletAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebSocketServerContainerInitializer.class)
static class JettyWebSocketConfiguration
@Bean
@ConditionalOnMissingBean(name = "websocketServletWebServerCustomizer")
JettyWebSocketServletWebServerCustomizer websocketServletWebServerCustomizer()
return new JettyWebSocketServletWebServerCustomizer();
可以看到,这里读取的WebSocketServletAutoConfiguration,metadata类型为SimpleAnnotationMetadata,底层使用的是ASM读取的类文件,所以并没有触发类加载。对于内部类,底层使用的也同样是SimpleMetadataReader进行读取,最终也会使用processConfigurationClass进行处理,且在构建ConfigurationClass时,使用了importBy为其外部类
new ConfigurationClass((MetadataReader) this.source, importedBy);
在processConfigurationClass中,首先看该配置类是否满足条件,阶段为PARSE_CONFIGURATION。这里JettyWebSocketConfiguration依赖WebSocketServerContainerInitializer类存在,由于不存在,所以是不生效的,但是这里并不会报错,因为底层实现conditionalOnClass时,没有使用类加载
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase)
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName()))
return false;
if (phase == null)
if (metadat以上是关于spring boot配置类注册深入解析的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot 动态数据源(Spring 注解数据源)