Filtering and loading of candidate classes during SpringBoot startup

Several initialization points:
When calling the SpringApplication constructor and the setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)) method, the underlying layer calls the loadSpringFactories method, loads the classes under spring.factories, caches them, and then puts the classes of the initialization type loaded into the list container. For subsequent calls

	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

During the startup process, SpringBoot loads into the main class, and according to the annotation information configured on the main class, the startup class is parsed by ConfigurationClassParser. During the parse process, includeFilters and excludeFilters are parsed, added to the type filter, and during the parsing process, an exclusion filter is added as an internal class. Convenient to exclude yourself, useful in the future,

Processing then includes @ComponentScan, @ComponentScans, and ClassPathBeanDefiniton Scanner for classes under package paths when parsing to basePackages. Wrap the corresponding class Resource as a Resource, then filter the class type to get a qualified candidate load class.

public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {

  private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
                        //Read Class Resources under Class Path
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				try {
					MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                                        //Classes are filtered here based on meta-information
					if (isCandidateComponent(metadataReader)) {
						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
						sbd.setSource(resource);
						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;
	}

        //Classes are filtered here based on metadata information
	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())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}
}


tf.match(metadataReader, getMetadataReaderFactory())), the method first makes its own judgment, if equal to itself, then excludes it. Default exclusion filters, including internal classes created by itself, AutoConfiguration ExcludeFilter, TypeExcludeFilter, three exclusion filters
The default include filter includes two annotation types, AnotationTypeFilter for @Component and @ManagedBean

	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {

		// This method optimizes avoiding unnecessary creation of ClassReaders
		// as well as visiting over those readers.

		if (matchSelf(metadataReader)) {
			return true;
		}
		ClassMetadata metadata = metadataReader.getClassMetadata();
		if (matchClassName(metadata.getClassName())) {
			return true;
		}

		if (this.considerInherited) {
			String superClassName = metadata.getSuperClassName();
			if (superClassName != null) {
				// Optimization to avoid creating ClassReader for super class.
				Boolean superClassMatch = matchSuperClass(superClassName);
				if (superClassMatch != null) {
					if (superClassMatch.booleanValue()) {
						return true;
					}
				}
				else {
					// Need to read super class to determine a match...
					try {
						if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
							return true;
						}
					}
					catch (IOException ex) {
						if (logger.isDebugEnabled()) {
							logger.debug("Could not read super class [" + metadata.getSuperClassName() +
									"] of type-filtered class [" + metadata.getClassName() + "]");
						}
					}
				}
			}
		}

		if (this.considerInterfaces) {
			for (String ifc : metadata.getInterfaceNames()) {
				// Optimization to avoid creating ClassReader for super class
				Boolean interfaceMatch = matchInterface(ifc);
				if (interfaceMatch != null) {
					if (interfaceMatch.booleanValue()) {
						return true;
					}
				}
				else {
					// Need to read interface to determine a match...
					try {
						if (match(ifc, metadataReaderFactory)) {
							return true;
						}
					}
					catch (IOException ex) {
						if (logger.isDebugEnabled()) {
							logger.debug("Could not read interface [" + ifc + "] for type-filtered class [" +
									metadata.getClassName() + "]");
						}
					}
				}
			}
		}

		return false;
	}

For the matchSelf(metadataReader) method to work, you need to add a custom method
The matchClassName(metadata.getClassName()) method calls its own added internal class and excludes itself from loading candidate classes because it is already in the beanDefinitionMap.
Wrapping a ScannedGenericBeanDefinition for a class that matches a candidate is the general Scanned BeanDefinition.
After scanning the candidates, place the BeanDefinition in the map.
After the @ComponentScan, @ComponentScans annotation classes are first parsed and loaded into candidate classes, check the scanned definition set for any further configuration classes, and recursively resolve them if needed. If the configuration classes are satisfied, the difficulty here is that the internal loop calling doProcessConfigurationClass() can cause developers to dizzy.

Continue to call ClassPathBeanDefinitonScanner for parse processing

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

Determine whether it is a configuration class

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

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

The isConfigurationCandidate method is called inside the method to determine if the metadata information satisfies ConfigurationCandidate and if it is a Configuration class, the hasBeanMethods method is called to determine if the @Bean annotation is included

abstract class ConfigurationClassUtils {

	static {
		candidateIndicators.add(Component.class.getName());
		candidateIndicators.add(ComponentScan.class.getName());
		candidateIndicators.add(Import.class.getName());
		candidateIndicators.add(ImportResource.class.getName());
	}

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

After resolving the @ComponentScan comment, let's move to the next heavyweight import class feature comment @Import

	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"));
		}
	}
		public Collection<SourceClass> getAnnotationAttributes(String annType, String attribute) throws IOException {
			Map<String, Object> annotationAttributes = this.metadata.getAnnotationAttributes(annType, true);
			if (annotationAttributes == null || !annotationAttributes.containsKey(attribute)) {
				return Collections.emptySet();
			}
			String[] classNames = (String[]) annotationAttributes.get(attribute);
			Set<SourceClass> result = new LinkedHashSet<>();
			for (String className : classNames) {
				result.add(getRelated(className));
			}
			return result;
		}

Get @Import for multiple internal class configurations by looping
The source class loaded here is retrieved recursively from the annotations. The source classes loaded according to the startup class @SpringBootApplication are

0 = {ConfigurationClassParser$SourceClass@3980} "com.bail.user.service.UserProviderBootstrap"
1 = {ConfigurationClassParser$SourceClass@4046} "org.springframework.boot.autoconfigure.SpringBootApplication"
2 = {ConfigurationClassParser$SourceClass@4017} "org.springframework.boot.SpringBootConfiguration"
3 = {ConfigurationClassParser$SourceClass@4035} "org.springframework.context.annotation.Configuration"
4 = {ConfigurationClassParser$SourceClass@3953} "java.lang.Object"
5 = {ConfigurationClassParser$SourceClass@4189} "org.springframework.boot.autoconfigure.EnableAutoConfiguration"
6 = {ConfigurationClassParser$SourceClass@4222} "org.springframework.boot.autoconfigure.AutoConfigurationPackage"
7 = {ConfigurationClassParser$SourceClass@4364} "org.springframework.context.annotation.ComponentScan"
8 = {ConfigurationClassParser$SourceClass@4497} "org.springframework.context.annotation.ImportResource"

First, get AutoConfiguration Packages$Registrar from source class:AutoConfiguration Packages
Get AutoConfiguration Packages$Registrar from source class:AutoConfiguration Packages
Load into the AutoConfiguration ImportSelector annotation according to EnableAutoConfiguration;
@SpringBootApplication consists of three notes

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

}


@Configuration
@Indexed
public @interface SpringBootConfiguration {
}

@Component
public @interface Configuration {
}

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)//Auto Configure Select Import Class
public @interface EnableAutoConfiguration {

}

//Automatic Configuration Package Registration Class
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

@Repeatable(ComponentScans.class)
public @interface ComponentScan {
}

After going through the getImports() method, two classes are loaded into:

0 = {ConfigurationClassParser$SourceClass@4618} "org.springframework.boot.autoconfigure.AutoConfigurationPackages$Registrar"
1 = {ConfigurationClassParser$SourceClass@4619} "org.springframework.boot.autoconfigure.AutoConfigurationImportSelector"

Then parse processImports on the loaded class
AutoConfiguration Packages$Registrar complies with ImportBeanDefinitionRegistrar.class, creates AutoConfiguration Packages$Registrar instance object, and then places ImportBeanDefinitionRegistrar with value=startup class in the map container;
AutoConfigurationImportSelector complies with the ImportSelector class. After creating the AutoConfigurationImportSelector instance selector, call deferredImportSelectorHandler to process the selector and add the selector to the list container;
This process simply places the instance object in its container.

The @ImportResource annotation is then resolved. The resolving resources are as follows:

"locations" -> {String[1]@5157} ["dubbo-provider.xml"]
"reader" -> {Class@3159} "interface org.springframework.beans.factory.support.BeanDefinitionReader"
"value" -> {String[1]@5159} ["dubbo-provider.xml"]

Store the resolved resource file in configClass Of map In a container.

Next, continue parsing@Bean annotation

After parsing is complete, it will be parsed to the configClass add to configurationClasses map In the type container.

Subsequent call this.deferredImportSelectorHandler.process()Yes importSelector Parse.
Where, ConfigurationClassParser Internal class with multiple import classes

class ConfigurationClassParser {

private static class ImportStack extends ArrayDeque<ConfigurationClass> implements ImportRegistry {

}

private class DeferredImportSelectorHandler {

}
private class DeferredImportSelectorGroupingHandler {
}
private static class DeferredImportSelectorHolder {
}
private static class DeferredImportSelectorGrouping {
}
private static class DefaultDeferredImportSelectorGroup implements Group {
}
}

Called internally DeferredImportSelectorGroupingHandler Of register Yes deferredImports Processing,
In parsing AutoConfigurationImportSlector During the process, a call was made to getImports()Method, and the underlying invocation of this method is SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass()Method:

```java
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}
}
	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
                //The different types of classes to load under the spring.factories file are already cached in the cache here
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

Here we only need to return classes of type org.springframework.boot.autoconfigure.EnableAutoConfiguration------------------------------------------------------------------------------------------------------------------------------
Several initialization points:
When calling the SpringApplication constructor and the setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)) method, the underlying layer calls the loadSpringFactories method, loads the classes under spring.factories, caches them, and then puts the classes of the initialization type loaded into the list container. For subsequent calls

	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

During the startup process, SpringBoot loads into the main class, and according to the annotation information configured on the main class, the startup class is parsed by ConfigurationClassParser. During the parse process, includeFilters and excludeFilters are parsed, added to the type filter, and during the parsing process, an exclusion filter is added as an internal class. Convenient to exclude yourself, useful in the future,

Processing then includes @ComponentScan, @ComponentScans, and ClassPathBeanDefiniton Scanner for classes under package paths when parsing to basePackages. Wrap the corresponding class Resource as a Resource, then filter the class type to get a qualified candidate load class.

public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {

  private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
                        //Read Class Resources under Class Path
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				try {
					MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                                        //Classes are filtered here based on meta-information
					if (isCandidateComponent(metadataReader)) {
						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
						sbd.setSource(resource);
						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;
	}

        //Classes are filtered here based on metadata information
	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())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}
}


tf.match(metadataReader, getMetadataReaderFactory())), the method first makes its own judgment, if equal to itself, then excludes it. Default exclusion filters, including internal classes created by itself, AutoConfiguration ExcludeFilter, TypeExcludeFilter, three exclusion filters
The default include filter includes two annotation types, AnotationTypeFilter for @Component and @ManagedBean

	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {

		// This method optimizes avoiding unnecessary creation of ClassReaders
		// as well as visiting over those readers.

		if (matchSelf(metadataReader)) {
			return true;
		}
		ClassMetadata metadata = metadataReader.getClassMetadata();
		if (matchClassName(metadata.getClassName())) {
			return true;
		}

		if (this.considerInherited) {
			String superClassName = metadata.getSuperClassName();
			if (superClassName != null) {
				// Optimization to avoid creating ClassReader for super class.
				Boolean superClassMatch = matchSuperClass(superClassName);
				if (superClassMatch != null) {
					if (superClassMatch.booleanValue()) {
						return true;
					}
				}
				else {
					// Need to read super class to determine a match...
					try {
						if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
							return true;
						}
					}
					catch (IOException ex) {
						if (logger.isDebugEnabled()) {
							logger.debug("Could not read super class [" + metadata.getSuperClassName() +
									"] of type-filtered class [" + metadata.getClassName() + "]");
						}
					}
				}
			}
		}

		if (this.considerInterfaces) {
			for (String ifc : metadata.getInterfaceNames()) {
				// Optimization to avoid creating ClassReader for super class
				Boolean interfaceMatch = matchInterface(ifc);
				if (interfaceMatch != null) {
					if (interfaceMatch.booleanValue()) {
						return true;
					}
				}
				else {
					// Need to read interface to determine a match...
					try {
						if (match(ifc, metadataReaderFactory)) {
							return true;
						}
					}
					catch (IOException ex) {
						if (logger.isDebugEnabled()) {
							logger.debug("Could not read interface [" + ifc + "] for type-filtered class [" +
									metadata.getClassName() + "]");
						}
					}
				}
			}
		}

		return false;
	}

For the matchSelf(metadataReader) method to work, you need to add a custom method
The matchClassName(metadata.getClassName()) method calls its own added internal class and excludes itself from loading candidate classes because it is already in the beanDefinitionMap.
Wrapping a ScannedGenericBeanDefinition for a class that matches a candidate is the general Scanned BeanDefinition.
After scanning the candidates, place the BeanDefinition in the map.
After the @ComponentScan, @ComponentScans annotation classes are first parsed and loaded into candidate classes, check the scanned definition set for any further configuration classes, and recursively resolve them if needed. If the configuration classes are satisfied, the difficulty here is that the internal loop calling doProcessConfigurationClass() can cause developers to dizzy.

Continue to call ClassPathBeanDefinitonScanner for parse processing

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

Determine whether it is a configuration class

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

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

The isConfigurationCandidate method is called inside the method to determine if the metadata information satisfies ConfigurationCandidate and if it is a Configuration class, the hasBeanMethods method is called to determine if the @Bean annotation is included

abstract class ConfigurationClassUtils {

	static {
		candidateIndicators.add(Component.class.getName());
		candidateIndicators.add(ComponentScan.class.getName());
		candidateIndicators.add(Import.class.getName());
		candidateIndicators.add(ImportResource.class.getName());
	}

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

After resolving the @ComponentScan comment, let's move to the next heavyweight import class feature comment @Import

	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"));
		}
	}
		public Collection<SourceClass> getAnnotationAttributes(String annType, String attribute) throws IOException {
			Map<String, Object> annotationAttributes = this.metadata.getAnnotationAttributes(annType, true);
			if (annotationAttributes == null || !annotationAttributes.containsKey(attribute)) {
				return Collections.emptySet();
			}
			String[] classNames = (String[]) annotationAttributes.get(attribute);
			Set<SourceClass> result = new LinkedHashSet<>();
			for (String className : classNames) {
				result.add(getRelated(className));
			}
			return result;
		}

Get @Import for multiple internal class configurations by looping
The source class loaded here is retrieved recursively from the annotations. The source classes loaded according to the startup class @SpringBootApplication are

0 = {ConfigurationClassParser$SourceClass@3980} "com.bail.user.service.UserProviderBootstrap"
1 = {ConfigurationClassParser$SourceClass@4046} "org.springframework.boot.autoconfigure.SpringBootApplication"
2 = {ConfigurationClassParser$SourceClass@4017} "org.springframework.boot.SpringBootConfiguration"
3 = {ConfigurationClassParser$SourceClass@4035} "org.springframework.context.annotation.Configuration"
4 = {ConfigurationClassParser$SourceClass@3953} "java.lang.Object"
5 = {ConfigurationClassParser$SourceClass@4189} "org.springframework.boot.autoconfigure.EnableAutoConfiguration"
6 = {ConfigurationClassParser$SourceClass@4222} "org.springframework.boot.autoconfigure.AutoConfigurationPackage"
7 = {ConfigurationClassParser$SourceClass@4364} "org.springframework.context.annotation.ComponentScan"
8 = {ConfigurationClassParser$SourceClass@4497} "org.springframework.context.annotation.ImportResource"

First, get AutoConfiguration Packages$Registrar from source class:AutoConfiguration Packages
Get AutoConfiguration Packages$Registrar from source class:AutoConfiguration Packages
Load into the AutoConfiguration ImportSelector annotation according to EnableAutoConfiguration;
@SpringBootApplication consists of three notes

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

}


@Configuration
@Indexed
public @interface SpringBootConfiguration {
}

@Component
public @interface Configuration {
}

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)//Auto Configure Select Import Class
public @interface EnableAutoConfiguration {

}

//Automatic Configuration Package Registration Class
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

@Repeatable(ComponentScans.class)
public @interface ComponentScan {
}

After going through the getImports() method, two classes are loaded into:

0 = {ConfigurationClassParser$SourceClass@4618} "org.springframework.boot.autoconfigure.AutoConfigurationPackages$Registrar"
1 = {ConfigurationClassParser$SourceClass@4619} "org.springframework.boot.autoconfigure.AutoConfigurationImportSelector"

Then parse processImports on the loaded class
AutoConfiguration Packages$Registrar complies with ImportBeanDefinitionRegistrar.class, creates AutoConfiguration Packages$Registrar instance object, and then places ImportBeanDefinitionRegistrar with value=startup class in the map container;
AutoConfigurationImportSelector complies with the ImportSelector class. After creating the AutoConfigurationImportSelector instance selector, call deferredImportSelectorHandler to process the selector and add the selector to the list container;
This process simply places the instance object in its container.

The @ImportResource annotation is then resolved. The resolving resources are as follows:

"locations" -> {String[1]@5157} ["dubbo-provider.xml"]
"reader" -> {Class@3159} "interface org.springframework.beans.factory.support.BeanDefinitionReader"
"value" -> {String[1]@5159} ["dubbo-provider.xml"]

Store the resolved resource file in configClass Of map In a container.

Next, continue parsing@Bean annotation

After parsing is complete, it will be parsed to the configClass add to configurationClasses map In the type container.

Subsequent call this.deferredImportSelectorHandler.process()Yes importSelector Parse.
Where, ConfigurationClassParser Internal class with multiple import classes

class ConfigurationClassParser {

private static class ImportStack extends ArrayDeque<ConfigurationClass> implements ImportRegistry {

}

private class DeferredImportSelectorHandler {

}
private class DeferredImportSelectorGroupingHandler {
}
private static class DeferredImportSelectorHolder {
}
private static class DeferredImportSelectorGrouping {
}
private static class DefaultDeferredImportSelectorGroup implements Group {
}
}

Called internally DeferredImportSelectorGroupingHandler Of register Yes deferredImports Processing,
In parsing AutoConfigurationImportSlector During the process, a call was made to getImports()Method, and the underlying invocation of this method is SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass()Method:

```java
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
				+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}
}
	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
                //The different types of classes to load under the spring.factories file are already cached in the cache here
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

Here we only need to return classes of type org.springframework.boot.autoconfigure.EnableAutoConfiguration
After loading the auto-assembly class, the filter to load the auto-assembly class is called sequentially, which includes three by default

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

These filtering conditions work based on annotation conditions on the auto-assembly class, and when filtering, by default, meta-annotation information is loaded from the configuration file below spring, which is located in the spring-autoconfigure-metadata.properties file, with 773 loaded by default.
Use class name + filter criteria as key s such as: org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration+ConditionalOnClass=org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.ConditionalOnClass,
In this project, after conditional filtering, there are 25 Configuration classes that meet the criteria

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.http.HttpMessageConvertersAutoConfiguration"
8 = "org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration"
9 = "org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration"
10 = "org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration"
11 = "org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration"
12 = "org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration"
13 = "org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration"
14 = "org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration"
15 = "org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration"
16 = "org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration"
17 = "org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration"
18 = "org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration"
19 = "org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration"
20 = "org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration"
21 = "org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration"
22 = "org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration"
23 = "org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration"
24 = "com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration"

This article focuses on the loading of DubboAutoConfiguration, which, when parsing the DubboAutoConfiguration configuration class, resolves that DubboAutoConfiguration contains three Bean instances

0 = {SimpleMethodMetadata@13417} "com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration.serviceAnnotationBeanPostProcessor(org.springframework.core.env.Environment)"
1 = {SimpleMethodMetadata@13418} "com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration.relaxedDubboConfigBinder()"
2 = {SimpleMethodMetadata@13419} "com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration.referenceAnnotationBeanPostProcessor()"

At this point, after parse() is called to resolve the configurations, BeanDefinition needs to be loaded based on the Configurations by calling

	private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

		if (trackedConditionEvaluator.shouldSkip(configClass)) {
			String beanName = configClass.getBeanName();
			if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
				this.registry.removeBeanDefinition(beanName);
			}
			this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
			return;
		}

		if (configClass.isImported()) {
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}

		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

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

Resolving dubbo_provider.xml uses the DefaultNamespaceHandlerResolver parser, which, when initialized, passes in the handler's file location

	/**
	 * The location to look for the mapping files. Can be present in multiple JAR files.
	 */
	public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

node nodes are resolved using a custom Namespace Handler, resolved by obtaining the corresponding DubboBeanDefinitionParser, and added to the BeanDefinitionMap container.
After the BeanDefinition is loaded, some BeanFactoryPostProcessor s are called for processing.

An internal call calls loadBeanDefinitionsFromRegistrars to register BeanDefinitions, uses ImportBeanDefinitionRegistrar internally to register BeanDefinition, and calls DubboConfigBindingsRegistrar, an inheritance class of ImportBeanDefinitionRegistrar to register BeanDefinition

Tags: Spring

Posted on Sun, 21 Nov 2021 13:22:58 -0500 by sdat1333