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进行深入解析,通过本文可以解答如下问题:

  1. 自动化配置类生效的机制,条件注解的处理过程
  2. spring boot自动化配置类,有的条件类不存在,工程里打开是标红的,spring boot解析时为什么不报错,又是如何通过编译的?
  3. @EnableXXXX到底是什么原理?注册BeanDefinition有哪些高端的姿势?
  4. @Configuration和@Component到底有什么区别。

    本文源码见:源码

    一张图总结整个配置类注册过程:

1. ConfigurationClassPostProcessor

    ConfigurationClassPostProcessor,实现PriorityOrdered,主要有两个作用:

  1. 在postProcessBeanDefinitionRegistry注入beanDefinition。
  2. 在postProcessBeanFactory,替换@Configuration class注解的beanClass为cglib增强。同时注入一个BeanPostProcessor:ImportAwareBeanPostProcessor

     核心逻辑如下:

  1. 先从当前BeanFactory找到所有含有@Configuration或者标有@Component/@ComponentScan/@Import/@ImportResource注解,或者有@Bean方法,作为初始化的配置类

  1. ConfigurationPhase.PARSE_CONFIGURATION阶段:使用ConfigurationClassParser,解析成所有的ConfigurationClass,包括其上的@import,以及@Bean method,全部解析并存到ConfigurationClass中。这个阶段一般不会load class。
  2. ConfigurationPhase.REGISTER_BEAN阶段:再把ConfigurationClass中的BeanMethod啥的注册到BeanDefinitionRegistry中,如@Import(ImportRegistrar),则直接register。这个阶段可能会load class,同时对于@Bean方法上的注解,会继续处理。
  3. 获取到新注册的未处理的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有三种:

  1. DeferredImportSelector,实例化后交给deferredImportSelectorHandler.handle处理,主要是分组进行聚合,然后排序,保证添加到beanDefinitionRegistry时是有序的,尤其是在@ConditionalOnMissingBean时,要覆盖,很有用。
  2. ImportSelector,实例化selectImport后,继续调用processImports,一般最后添加的也是普通配置类
  3. ImportBeanDefinitionRegistrar,实例话后,添加到configClass中,待会进行registerBean。
  4. 普通配置类,继续调用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 Boot 动态数据源(Spring 注解数据源)

Spring Boot 多数据源 自动切换

【Spring】简述@Configuration配置类注册BeanDefinition到Spring容器的过程

Dubbo启动源码解析一

Spring Boot自动配置源码解析(基于Spring Boot 2.0.2.RELEASE)