Spring Boot配置类处理

2023-05-16

ConfigurationClassPostProcessor处理所有配置类,包括@Component、@Bean、@Import注解等。由于配置类可能会引入新的配置类,新的配置类也需要被处理,所以ConfigurationClassPostProcessor采用do-while循环处理配置类,直到不存在未处理的配置类。

配置类的具体的处理工作交给ConfigurationClassParser,配置类引入新的配置类主要分两种情况:
1、配置类的Member Class是配置类、@ComponentScan扫描到配置类等情况,这些导入到rigistry的新配置类会递归的继续处理,并放到ConfigurationClassParser的configurationClasses属性中,这些都是已经处理过的
2、@Bean、@ImportResource、ImportBeanDefinitionRegistrar等不会直接注册到registry,而是由ConfigurationClassBeanDefinitionReader load到registry,这些配置类没有被处理过

配置类处理核心逻辑

这里的核心逻辑比较简单,如上所述,主要在ConfigurationClassPostProcessor#processConfigBeanDefinitions方法

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		String[] candidateNames = registry.getBeanDefinitionNames();

        // 过滤出beanFactory中的配置类
		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;
		}

		// 排序
		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();
		}

		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

        // candidates 待解析的配置类,alreadyParsed已经解析过的配置类
		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
			
            // 处理每个配置类,@Component、@Import、@ComponentScan、@Bean注解等都在这里处理
            parser.parse(candidates);
			parser.validate();

            // parser.getConfigurationClasses()获取到的配置类都被处理过
			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());
			}
            // loadBeanDefinitions,@Bean、@ImportResource、ImportBeanDefinitionRegistrar注册bean
            // 会在这里load到registry,这部分配置类未被处理,后面详细介绍
			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);
			processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();

			candidates.clear();
            // 说明导入了新的bean,可能存在未parse的配置类
			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());
				}
                // 将未parse的配置类放入candidates,下一轮do-while循环处理
				for (String candidateName : newCandidateNames) {
					if (!oldCandidateNames.contains(candidateName)) {
						BeanDefinition bd = registry.getBeanDefinition(candidateName);
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(bd.getBeanClassName())) {
							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();
		}
	}

下面重点介绍do-while循环中两个重要步骤:parse和loadBeanDefinitions

parser.parse(candidates)

在外层的do-while循环中,将一组配置类解析的具体工作交给ConfigurationClassParser。该类的处理存在递归调用,比较复杂,调用链路简图如下
配置类解析调用链路

解析一组配置类

parse(Set)处理一组配置类,该方法在ConfigurationClassPostProcessor在do-while循环中调用。该方法在for循环中逐个解析所有配置类,最后调用一下deferredImportSelectorHandler,用来处理DeferredImportSelector。

	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();
	}

解析一个配置类

三个parse方法都调用了processConfigurationClass方法,parse如下,仅仅是将不同的入参统一成ConfigurationClass

	protected final void parse(@Nullable String className, String beanName) throws IOException {
		Assert.notNull(className, "No bean class name for configuration class bean definition");
		MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
		processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
	}

	protected final void parse(Class<?> clazz, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
	}

	protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
	}

processConfigurationClass方法处理了三件事情
1、如果当前configClass存在existingClass(配置类已经处理过),如果existingClass和configClass都是被其他配置类导入的,则合并被导入的类,不处理configClass;如果configClass不是被导入的,则重新处理这个类
2、do-while循环遍历一个配置类的所有父类,调用doProcessConfigurationClass处理配置类及每个父类
3、处理完配置类后加入Map configurationClasses

	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);
	}

配置类的具体解析

doProcessConfigurationClass具体处理每个配置类或其父类,配置类即configClass,当前要处理的类即sourceClass。doProcessConfigurationClass主要获取到sourceClass类、接口、方法、属性上的注解,并进行特定处理,下面分别介绍

1、处理MemberClasses

标注了@Component都会被处理,MemberClass就是内部类

		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			processMemberClasses(configClass, sourceClass, filter);
		}

processMemberClasses获取sourceClass的所有是配置类的MemberClass,调用processConfigurationClass当作配置类处理,candidate.asConfigClass(configClass)指定了内部类是被哪个配置类导入的。这里会存在递归调用

	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();
					}
				}
			}
		}
	}

2、处理@PropertySource

PropertySource作用是导入配置文件

3、处理@ComponentScan

通过componentScanParser扫描bean,对扫到的配置类调用parse(String, String)重载方法进行解析,这里也存在递归调用

		// 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());
					}
				}
			}
		}

4、处理@Import

处理@Import注解比较复杂,首先从sourceClass查找Import进来的类,然后分情况处理这些类。

查找Import类

通过尾递归查找所有导入的类

	private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
		Set<SourceClass> imports = new LinkedHashSet<>();
		Set<SourceClass> visited = new LinkedHashSet<>();
		collectImports(sourceClass, imports, visited);
		return imports;
	}

递归方法collectImports。获取sourceClass上所有注解,如果注解不是@Import,继续搜索collectImports查找注解上的@Import;如果注解是@Import,获取Import进来的类,加入到imports

	private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
			throws IOException {

		if (visited.add(sourceClass)) {
			for (SourceClass annotation : sourceClass.getAnnotations()) {
				String annName = annotation.getMetadata().getClassName();
				if (!annName.equals(Import.class.getName())) {
					collectImports(annotation, imports, visited);
				}
			}
			imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
		}
	}

Import类处理

for循环对所有Import进来的类,分四种情况处理

  • 如果是DeferredImportSelector,调用deferredImportSelectorHandler#handle处理,细节后续介绍
  • 如果是普通的ImportSelector,实例化该Selector并selectImport,调用processImports递归处理这些类
  • 如果是ImportBeanDefinitionRegistrar,调用configClass#addImportBeanDefinitionRegistrar暂存该Registrar,后续通过ConfigurationClassBeanDefinitionReader再调用该registrat注册类
  • 如果都不是以上情况,则当作一个配置类调用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) {
							this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
						}
						else {
							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();
			}
		}
	}

5、处理@ImportResource

resource里面可能定义了BeanDefinition,通过该注解导入resource。处理该注解仅将resource暂存在configClass里,后续再通过ConfigurationClassBeanDefinitionReader来注册BeanDefinition

		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);
			}
		}

6、处理@Bean

处理@Bean首先解析出beanMethod,然后暂存在configClass里,后续通过ConfigurationClassBeanDefinitionReader注册BeanDifinition

		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

7、处理接口

递归处理所有接口及父接口的@Bean注解

	private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
		for (SourceClass ifc : sourceClass.getInterfaces()) {
			Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
			for (MethodMetadata methodMetadata : beanMethods) {
				if (!methodMetadata.isAbstract()) {
					// A default method or other concrete method on a Java 8+ interface...
					configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
				}
			}
			processInterfaces(configClass, ifc);
		}
	}

deferredImportSelectorHandler

deferredImportSelectorHandler用来处理DeferredImportSelector,有两个重要方法:
1、processImports的时候,如果遇到DeferredImportSelector,调用deferredImportSelectorHandler#handle进行处理
2、parse配置类时候,处理完所有配置类,调用deferredImportSelectorHandler#process方法统一处理DeferredImportSelector

DefferedImportSelector与ImportSelector的区别
processorImports时遇到ImportSelector,会立即selectImport;而对于DefferedImportSelector,会先暂存起来,处理完其他的配置类再统一处理DefferedImportSelector。当使用@Conditional注解注册beanDefinition会很有用。

handle和process方法

handler和process方法目的是实现deffered语义,即延迟处理,延迟到当前parse过程的其他配置类都处理过之后,再处理DefferedImportSelector。

handle方法
processImports的过程中会调用handle方法,如果 deferredImportSelectors == null,意味着process, 正在进行(开始process之前会将deferredImportSelectors置为null,这个方法被调用即处理DeferredImportSelector的过程中又import了DeferredImportSelector),此时则直接处理,DeferredImportSelector;否则直接加入list deferredImportSelectors中,等待process处理

process方法
开始process之前,将deferredImportSelectors置为null。然后实例化DeferredImportSelectorGroupingHandler,对所有的DefferedImportSelector注册到handler里,然后调用DeferredImportSelectorGroupingHandler#processGroupImports。

private class DeferredImportSelectorHandler {

    	// 暂存的deferredImportSelector
		@Nullable
		private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();

		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 {
				this.deferredImportSelectors.add(holder);
			}
		}

		public void process() {
			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
			this.deferredImportSelectors = null;
			try {
				if (deferredImports != null) {
					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
					deferredImports.forEach(handler::register);
					handler.processGroupImports();
				}
			}
			finally {
				this.deferredImportSelectors = new ArrayList<>();
			}
		}
	}

DeferredImportSelectorGroupingHandler

process过程中,将处理逻辑委托给DeferredImportSelectorGroupingHandler,先register、然后processGroupImports,目的是实现Group语义:即多个DefferedImportSelector为一组,这多个DefferedImportSelector import进来的类可以放在一起,通过特定逻辑决定哪些最终要import进来

先看下DeferredImportSelector接口定义

DeferredImportSelector
public interface DeferredImportSelector extends ImportSelector {

    // 相同Class<? extends Group>为相同的组,为null是DefaultDeferredImportSelectorGroup
	@Nullable
	default Class<? extends Group> getImportGroup() {
		return null;
	}
    
	interface Group {

        // 一个group中包含多个DeferredImportSelector,会调用该方法逐个处理。处理时需要暂存
        // 每个DeferredImportSelector import进来的类
		void process(AnnotationMetadata metadata, DeferredImportSelector selector);

        // 调用process处理所有DeferredImportSelector之后,会调用该方法获取最终要import的类,
        // 这里面可以实现特定过滤逻辑或其他逻辑
		Iterable<Entry> selectImports();

        // 封装import的一个className。metadata是配置类的metadata,importClassName是将要import
        // 的className
		class Entry {

			private final AnnotationMetadata metadata;

			private final String importClassName;

			public Entry(AnnotationMetadata metadata, String importClassName) {
				this.metadata = metadata;
				this.importClassName = importClassName;
			}
	}

}

Group语义是在DeferredImportSelectorGrouping类实现的,如下

DeferredImportSelectorGrouping

DeferredImportSelectorGrouping封装了Group实例以及多个DeferredImportSelector。getImports调用group#process逐个处理所有的DeferredImportSelector,然后调用group#selectImports获取import的所有类

	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);
		}

		public Iterable<Group.Entry> getImports() {
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			return this.group.selectImports();
		}
	}

默认的Group是DefaultDeferredImportSelectorGroup
process方法就是简单调用selector#selectImports方法,并暂存起来;selectImports方法即返回所有selector import进来的类

	private static class DefaultDeferredImportSelectorGroup implements Group {

		private final List<Entry> imports = new ArrayList<>();

		@Override
		public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
			for (String importClassName : selector.selectImports(metadata)) {
				this.imports.add(new Entry(metadata, importClassName));
			}
		}

		@Override
		public Iterable<Entry> selectImports() {
			return this.imports;
		}
	}

另一个实现是AutoConfigurationGroup
process方法也是暂存了所有import的类;selectImports包含了特定的过滤逻辑

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<>();


		@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());
		}
	}

最后看下DeferredImportSelectorGroupingHandler的register方法和processGroupImports方法

DeferredImportSelectorGroupingHandler实现细节

groupings属性
handler会处理所有的DeferredImportSelector,这个map按分组保存了DeferredImportSelector
key:如果DeferredImportSelector的group不为null,则key是group的Class对象;如果group是null,则key是该DeferredImportSelectorHolder对象。key本身没有作用,仅仅是通过key对所有的DeferredImportSelector分个组,后续对groups.values()进行遍历
value:是DeferredImportSelectorGrouping对象


	private class DeferredImportSelectorGroupingHandler {

		private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();

		private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();

        // 将DeferredImportSelector进行分组,并创建DeferredImportSelectorGrouping
		public void register(DeferredImportSelectorHolder deferredImport) {
			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() {
            // 对分好组的DeferredImportSelector分组处理
			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
				Predicate<String> exclusionFilter = grouping.getCandidateFilter();
                
                // 调用了DeferredImportSelectorGrouping#getImports方法。如上,里面调用了
                // group#process、group#selectImports,通过Group的特定化逻辑获取到了这一组
                // DeferredImportSelector import的类。然后forEach对所有的类通过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);
					}
				});
			}
		}
		
        // 实例化group,如果DeferredImportSelector指定了Group类,则用指定的类实例化,否则是默认的
        // DefaultDeferredImportSelectorGroup
		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);
		}
	}

reader.loadBeanDefinitions(configClasses)

parse的过程中,有几个地方导入的类会暂存在configClass中,再由ConfigurationClassBeanDefinitionReader load进beanFactory
1、processImports过程中,遇到ImportBeanDefinitionRegistrar

        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());
        }
	private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
		registrars.forEach((registrar, metadata) ->
				registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
	}

2、@ImportResource

		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);
			}
		}

如果@ImportResource注解指定了BeanDefinitionReader类名,则根据配置文件后缀使用GroovyBeanDefinitionReader、XmlBeanDefinitionReader,使用reader来load

	private void loadBeanDefinitionsFromImportedResources(
			Map<String, Class<? extends BeanDefinitionReader>> importedResources) {

		Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();

		importedResources.forEach((resource, readerClass) -> {
			// Default reader selection necessary?
			if (BeanDefinitionReader.class == readerClass) {
				if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
					// When clearly asking for Groovy, that's what they'll get...
					readerClass = GroovyBeanDefinitionReader.class;
				}
				else if (shouldIgnoreXml) {
					throw new UnsupportedOperationException("XML support disabled");
				}
				else {
					// Primarily ".xml" files but for any other extension as well
					readerClass = XmlBeanDefinitionReader.class;
				}
			}

			BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
			if (reader == null) {
				try {
					// Instantiate the specified BeanDefinitionReader
					reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
					// Delegate the current ResourceLoader to it if possible
					if (reader instanceof AbstractBeanDefinitionReader) {
						AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
						abdr.setResourceLoader(this.resourceLoader);
						abdr.setEnvironment(this.environment);
					}
					readerInstanceCache.put(readerClass, reader);
				}
				catch (Throwable ex) {
					throw new IllegalStateException(
							"Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
				}
			}

			// TODO SPR-6310: qualify relative path locations as done in AbstractContextLoader.modifyLocations
			reader.loadBeanDefinitions(resource);
		});
	}

3、@Bean

		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Boot配置类处理 的相关文章

随机推荐

  • 附录5:CentOS、Anolis 龙蜥 源码编译 异常处理

    以语雀 文档为准 异常 xff1a bin sh cc span class token builtin class name command span not found 解决 xff1a yum y span class token f
  • 关于angularJS绑定数据时自动转义html标签

    对于前后台分离开发 xff0c 在使用angular为开发框架的时候 xff0c 如果后台在返回数据的时候包含html格式的数据 xff0c angularJS在进行数据绑定时默认是会以文本的形式输出 xff0c 也就是对你数据中的html
  • 附录1:配置 containerd 加速

    以语雀 文档为准 说明 不推荐在 k8s 网络配置前加速 xff0c 可能会导致无法拉取镜像 xff08 原因可能是镜像的原因 xff0c 或者是配置的原因 xff0c 可以考虑事先或报错时手动拉取镜像 xff09 安装 span clas
  • 附录2:CentOS 下载

    以语雀 文档为准 视频演示 点击查看 bilibili CentOS Vault Mirror CentOS 7 8 最新版 xff08 含各大云厂商的镜像 xff09 CentOS 7 http isoredirect centos or
  • 附录3:Anolis 龙蜥 下载

    以语雀 文档为准 视频演示 点击查看 bilibili 官网 xff1a https openanolis cn 7 7 正式版 https mirrors openanolis cn anolis 7 7 isos GA x86 64 7
  • 附录4:Ubuntu 乌班图 下载

    以语雀 文档为准 https releases ubuntu com https old releases ubuntu com releases 版本号 版本代号 下载地址 14 04 5 Trusty Tahr https old re
  • 小工具1:tcping

    以语雀 文档为准 说明 tcping exe 是一个控制台应用程序 xff0c 其操作类似于 ping xff0c 但它在tcp端口上工作 在 Windows 上要查看指定 IP 端口 是否连通 xff0c 通常要使用 telnet xff
  • 小工具2:Windows curl.exe

    以语雀 文档为准 GitHub 仓库 xff1a https github com curl curlGitHub 仓库 Windows 版 xff1a https github com curl curl for win下载页面 xff1
  • 小工具3:Windows tail.exe

    以语雀 文档为准 文件 xff1a tail zip xff0c 文件来源于网络 xff0c 用于实时查看文件内容 xff0c 与 Linux 的 tail 有相同的用法 下载上述文件 xff0c 解压 xff0c 将 tail exe 放
  • 小工具4:Windows cports.exe

    以语雀 文档为准 文件 cports zip xff0c 文件来源于网络 xff0c 用于实时查看端口占用情况 xff0c 使用管理员权限运行时 xff0c 可杀端口 下载上述文件 xff0c 解压 xff0c 将 cports exe 放
  • 为何在 node 项目中使用固定版本号,而不使用 ~、^?

    以语雀 文档为准 使用 时吃过亏希望版本号掌握在自己手里 xff0c 作者自己升级 xff08 跟随官方进行升级 xff0c 就算麻烦作者 xff0c 也不想麻烦使用者 xff09 虽然 pnpm 很好用 xff0c 但是不希望在项目中用到
  • 导读 2、kubernetes(k8s)导读

    以语雀 文档为准 内容来自 Kubernetes 权威指南 第5版等 kubernetes 是什么 xff1f kubernetes是一个全新的基于容器技术的分布式架构领先方案 xff0c 是容器云的优秀平台选型方案 xff0c 已经成为新
  • 区块链——脱坑truffle

    使用truffle构建一个智能合约 实现输出 helloworld 的功能 网上有很多帖子 但也有很多坑 这里展示我的搭建过程 帮助大家绕过那些麻烦 一 安装web3 solc truffle npm g install solc npm
  • 使用fescar遇到can not register RM,err:can not connect to fescar-server.

    前提 如果你看到了这篇文章 xff0c 说明你已经成功跑起了阿里爸爸fescar官方提供的example和server xff0c 并且你的客户端和服务端之间心跳正常 xff0c 但是当客户端进行事务操作时却提示以下错误 com span
  • Arch常用软件

    常用软件 开发 gitandroid studiovim 图形界面 网络 pacman S networkmanager 启动其提供的服务 systemctl start NetworkManager service 让服务开机自启 sys
  • CentOS可以使用的yum仓库地址

    name 61 CentOS 6 6 Base baseurl 61 http mirrors aliyun com centos vault 6 6 os x86 64 gpgcheck 61 1 gpgkey 61 http mirro
  • pyqt错误:Unresolved reference 和 正确安装pycharm+python+pyqt5

    写如下代码 xff1a span class hljs keyword import span sys span class hljs keyword from span PyQt5 span class hljs keyword impo
  • 以下是adb工具包最新Google官方版下载地址:

    以下是adb工具包最新Google官方版下载地址 xff1a ADB和Fastboot for Windows https dl google com android repository platform tools latest win
  • java.lang.UnsupportedOperationException 及resultMap相关知识

    起因 今天在请求数据库数据时报错java lang UnsupportedOperationException xff0c 从字面上理解错误的含义就是不支持的操作异常 xff0c 后面定位到错误发生在数据库sql语句中 xff0c 具体原因
  • Spring Boot配置类处理

    ConfigurationClassPostProcessor处理所有配置类 xff0c 包括 64 Component 64 Bean 64 Import注解等 由于配置类可能会引入新的配置类 xff0c 新的配置类也需要被处理 xff0