SpringBoot 源码解析5:ConfigurationClassPostProcessor整体流程和@ComponentScan源码分析

吃透Spring源码(十六):ConfigurationClassPostProcessor详细介绍 一,ConfigurationClassPostProcessor简介 ConfigurationClassPostProcessor是一个后置处理器的类,主要功能是参与BeanFactory的建造,主要功能如下: 解析加了@Configuration的配置类 解析@ComponentScan扫描的包 解析@ComponentScans扫描的包 解析@Import注解 ConfigurationClassPostProcessor类图: ConfigurationClassPostProcessor 阅读详情

1. 知道以下几点,读ConfigurationClassPostProcessor源码会更轻松

  1. 配置类后置处理器ConfigurationClassPostProcessor,是对Spring注解式配置支持的核心,负责对@Component、@ComponentScan、@Import等注解的解析,将BeanDefinition注册到beanFactory。
  2. ConfigurationClassPostProcessor是在AnnotationConfigServletWebServerApplicationContext创建的时候注册的。参考 SpringBoot 源码解析2:启动流程
//AnnotationConfigUtils#registerAnnotationConfigProcessors
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
	RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
	def.setSource(source);
	beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
  1. BeanDefinitionRegistryPostProcessor实现了BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor。ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry在刷新容器的时候AbstractApplicationContext#invokeBeanFactoryPostProcessors方法中调用。参考 SpringBoot 源码解析4:refresh 方法解析

2. 源码解析 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

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

registry其实就是就是beanFactory,一个beanFactory只会处理一次

2.1 ConfigurationClassPostProcessor#processConfigBeanDefinitions

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	//存放有@Configuration的BeanDefinitionHolder
	List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
	//获取到手动注册的BeanDefinition,在此之前已经将启动类注册了,请参考springBoot启动流程
	String[] candidateNames = registry.getBeanDefinitionNames();

	for (String beanName : candidateNames) {
		BeanDefinition beanDef = registry.getBeanDefinition(beanName);
		//判断BeanDefinition是否存在ConfigurationClassPostProcessor.configurationClass属性
		if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
			}
		}
		// 不存在ConfigurationClassPostProcessor.configurationClass,就会配置此属性。
		// 配置里@Configuration就会返回true,才会放入到候选配置里面
		else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
			configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
		}
	}

	// Return immediately if no @Configuration classes were found
	// 没有发现@Configuration,就会立马返回
	if (configCandidates.isEmpty()) {
		return;
	}

	// Sort by previously determined @Order value, if applicable
	// 按照@Order排序
	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
	// 探测有没有自定义的BeanName生成器,如果有的话就使用自定义的beanName生成器
	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
	// 创建解析器,解析每一个有@Configuration的类
	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 {
		// 解析有@Configuration的BeanDefinition
		parser.parse(candidates);
		// 对Configuration和Bean注解进行了校验
		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());
		}
		// 此时有一些BeanDefinition没有被注册,比如@Bean、@Import中的ImportBeanDefinitionRegistrar
		this.reader.loadBeanDefinitions(configClasses);
		alreadyParsed.addAll(configClasses);

		candidates.clear();
		// 这是在一个循环里面,判断bean工厂在解析前和解析后是否有新注册的BeanDefinition
		// 如果有新注册的BeanDefinition,那么这些BeanDefinition也有可能是配置类,就会去解析这些BeanDefinition
		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())) {
						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();
	}
}
  1. 获取到所有注册到的BeanDefinition,通过ConfigurationClassPostProcessor.configurationClass属性,判断BeanDefinition有没有被处理过。下面会讲ConfigurationClassUtils#checkConfigurationClassCandidate。
  2. 如果没有处理,那么就会调用 ConfigurationClassUtils#checkConfigurationClassCandidate判断当前的BeanDefinition是否为配置类。只有当它为配置类,后面的配置类解析器才会对它进行解析。
  3. 兼容了自定义的Bean的名称生成器,可以使用用户自定义的生成器。
  4. 创建配置类解析器ConfigurationClassParser,对已排好序的BeanDefinitionHolder进行解析。
  5. 对Configuration和Bean注解进行一些校验。
  6. 判断bean工厂在解析前和解析后是否有新注册的BeanDefinition。如果有新注册的BeanDefinition,这些BeanDefinition有可能是配置类,那么就会通过ConfigurationClassParser去解析这些BeanDefinition。
2.1.1 ConfigurationClassUtils#checkConfigurationClassCandidate
校验当前的BeanDefinition是否为配置类
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.
		// 这些类型Spring不可作为配置类
		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注解的属性
	Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
	if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
		// Configuration 注解的proxyBeanMethods属性为true,则为BeanDefinition为full,会被代理
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
	}
	// 当前的BeanDefinition"能作为配置类",配置属性为lite,不被代理
	else if (config != null || isConfigurationCandidate(metadata)) {
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
	}
	else {
		// Configuration注解不存在
		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;
}
  1. 判断了BeanFactoryPostProcessor、BeanPostProcessor、AopInfrastructureBean、EventListenerFactory类型不能作为配置类。
  2. 如果当前BeanDefintion没有@Configuration,就不能作为配置类。
  3. 如果有@Configuration并且proxyBeanMethods属性为true,能作为配置类,就会配置为full。为full的会被代理。
  4. 如果有@Configuration并且proxyBeanMethods属性为false,并且判断 “能作为配置类”,就会配置为lite。lite不会被代理。
  5. 这里简单提一下@Configuration代理过程,如果Configuration的proxyBeanMethods属性为true,那么当前的BeanDefinition为full,就会在ConfigurationClassPostProcessor#enhanceConfigurationClasses会创建代理类,从而创建代理对象。代理对象在调用beanMethod(@Bean方法)时,会被BeanMethodInterceptor拦截器拦截。代理拦截器的逻辑是这样的:在第一次调用beanMethod完毕后生成bean之后,会根据类名和方法名称生成一个缓存,第二次生成调用同一个beanMethod,会直接从缓存中取,而不是调用beanMethod去创建对象。
  6. 那么,什么样"能作为配置类"呢?
2.1.2 ConfigurationClassUtils#isConfigurationCandidate
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 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...
	try {
		return metadata.hasAnnotatedMethods(Bean.class.getName());
	}
	catch (Throwable ex) {
		if (logger.isDebugEnabled()) {
			logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
		}
		return false;
	}
}

能作为配置类的条件:首先,接口肯定是不能作为配置类的。类上有Component、ComponentScan、Import、ImportResource,或者方法中有@Bean注解的,都能作为配置类。

2.2 ConfigurationClassParser#parse

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

在解析完配置类之后,才会对deferredImportSelector进行处理。

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

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
	// 支持Conditional注解
	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);
	do {
		sourceClass = doProcessConfigurationClass(configClass, sourceClass);
	}
	while (sourceClass != null);

	this.configurationClasses.put(configClass, configClass);
}
  1. configurationClasses中存放着已解析的配置信息,如果没有加载,则调用ConfigurationClassParser#doProcessConfigurationClass方法解析配置类,解析完毕之后放入到configurationClasses。
  2. 如果已解析并且importedBy不为空,则合并importedBy。如果不是Import,则清除缓存,重新解析。
2.2.2 ConfigurationClassParser#doProcessConfigurationClass
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
		throws IOException {

	// 处理@Component注解,处理当前类声明的类DeclaredClasse,因为当前类的内部类也有可能是配置类
	if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
		// Recursively process any member (nested) classes first
		processMemberClasses(configClass, sourceClass);
	}

	// Process any @PropertySource annotations
	// 处理@PropertySource 注解
	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
	// 处理@ComponentScan注解 
	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
	// 处理@Import注解
	processImports(configClass, sourceClass, getImports(sourceClass), true);

	// Process any @ImportResource annotations
	// 处理@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);
		}
	}

	// Process individual @Bean methods
	// 处理@Bean注解
	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;
}
  1. 此方法返回的是父类的资源,如果没有父类,说明类当前类已解析完毕。
  2. 对Component、PropertySource、ComponentScan、Import 、ImportResource、Bean等注解进行了解析。
  3. 我们最关心的是@ComponentScan和@Component,下面我们就对它们进行源码分析。

3. @ComponentScan 源码分析

在创建ConfigurationClassParser的时候,就创建了componentScanParser。
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
		ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
		BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {

	this.metadataReaderFactory = metadataReaderFactory;
	this.problemReporter = problemReporter;
	this.environment = environment;
	this.resourceLoader = resourceLoader;
	this.registry = registry;
	this.componentScanParser = new ComponentScanAnnotationParser(
			environment, resourceLoader, componentScanBeanNameGenerator, registry);
	this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}

3.1 ComponentScanAnnotationParser#parse

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
	// 创建扫描器,默认配置了三个includeFilters。Component、ManagedBean、Named。
	ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
			componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

	Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
	boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
	scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
			BeanUtils.instantiateClass(generatorClass));

	ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
	if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
		scanner.setScopedProxyMode(scopedProxyMode);
	}
	else {
		Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
		scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
	}

	scanner.setResourcePattern(componentScan.getString("resourcePattern"));

	for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
		for (TypeFilter typeFilter : typeFiltersFor(filter)) {
			scanner.addIncludeFilter(typeFilter);
		}
	}
	for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
		for (TypeFilter typeFilter : typeFiltersFor(filter)) {
			scanner.addExcludeFilter(typeFilter);
		}
	}

	boolean lazyInit = componentScan.getBoolean("lazyInit");
	if (lazyInit) {
		scanner.getBeanDefinitionDefaults().setLazyInit(true);
	}

	Set<String> basePackages = new LinkedHashSet<>();
	String[] basePackagesArray = componentScan.getStringArray("basePackages");
	for (String pkg : basePackagesArray) {
		String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
		Collections.addAll(basePackages, tokenized);
	}
	for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
		basePackages.add(ClassUtils.getPackageName(clazz));
	}
	//如果componentScan没有配置basePackages和basePackageClasses属性,那么就取声明@ComponentScan的类所对应的包名
	if (basePackages.isEmpty()) {
		basePackages.add(ClassUtils.getPackageName(declaringClass));
	}

	scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
		@Override
		protected boolean matchClassName(String className) {
			return declaringClass.equals(className);
		}
	});
	// 扫描包
	return scanner.doScan(StringUtils.toStringArray(basePackages));
}
  1. 此方法只是对扫描器进行了配置,扫描前的准备工作。
  2. ComponentScan扫描的时候有两种过滤器,excludeFilters比includeFilters优先级高:
    2.1. includeFilters:includeFilters匹配到的会注册到bean工厂。
    2.2. excludeFilters:excludeFilters匹配到的不会注册到bean工厂。
  3. ClassPathBeanDefinitionScanner如果使用默认的过滤器,就会添加三个注解式的includeFilter:Component、ManagedBean、Named。
protected void registerDefaultFilters() {
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
	try {
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
		logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
		// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
	}
	try {
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
		logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
		// JSR-330 API not available - simply skip.
	}
}
  1. 如果@componentScan没有配置basePackages和basePackageClasses属性,那么就取声明@ComponentScan的class所对应的包名。

3.2 ClassPathBeanDefinitionScanner#doScan

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) {
		//获取到basePackage下面所有的beanDefinition
		Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
		for (BeanDefinition candidate : candidates) {
			//解析Scope注解,默认singleton不代理
			ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
			candidate.setScope(scopeMetadata.getScopeName());
			//通过bean的名称生成器生成beanName,默认的是AnnotationBeanNameGenerator。解析Component、ManagedBean、Named的value属性。如果以上注解属性,则AnnotationBeanNameGenerator#buildDefaultBeanName创建默认的beanName,如果类名的前两个字母是大写,则是类的名称,否则就将首字母变成小写。
			String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
			if (candidate instanceof AbstractBeanDefinition) {
				//处理beanDefinition,对BeanDefinition设置了默认的属性
				postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
			}
			if (candidate instanceof AnnotatedBeanDefinition) {
				//解析公共的注解Lazy、Primary、DependsOn、Role、Description,对BeanDefinition的属性进行修改。
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
			if (checkCandidate(beanName, candidate)) {
				BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
				//Scope代理处理,如果需要代理,则已经注册了原始的BeanDefinition,返回的是代理的BeanDifinition。
				definitionHolder =
						AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
				beanDefinitions.add(definitionHolder);
				//注册BeanDefinition
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}
  1. 获取到basePackage下面所有的资源,并且解析成BeanDefinition。
  2. 对@Scope注解进行解析,判断是否需要代理,默认singleton不代理。
  3. 通过bean的名称生成器生成beanName,默认的是AnnotationBeanNameGenerator。解析Component、ManagedBean、Named的value属性,如果属性存在则属性为bean的名称;如果属性不存在,则AnnotationBeanNameGenerator#buildDefaultBeanName创建默认的beanName,如果类的shortName前两个字母是大写,则是shortName,否则就将shortName的首字母变成小写。
  4. 处理beanDefinition,对BeanDefinition设置了默认的属性。
  5. 解析公共的注解Lazy、Primary、DependsOn、Role、Description,对BeanDefinition的属性进行修改。
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
	AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
	if (lazy != null) {
		abd.setLazyInit(lazy.getBoolean("value"));
	}
	else if (abd.getMetadata() != metadata) {
		lazy = attributesFor(abd.getMetadata(), Lazy.class);
		if (lazy != null) {
			abd.setLazyInit(lazy.getBoolean("value"));
		}
	}

	if (metadata.isAnnotated(Primary.class.getName())) {
		abd.setPrimary(true);
	}
	AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
	if (dependsOn != null) {
		abd.setDependsOn(dependsOn.getStringArray("value"));
	}

	AnnotationAttributes role = attributesFor(metadata, Role.class);
	if (role != null) {
		abd.setRole(role.getNumber("value").intValue());
	}
	AnnotationAttributes description = attributesFor(metadata, Description.class);
	if (description != null) {
		abd.setDescription(description.getString("value"));
	}
}
  1. Scope代理处理,如果需要代理,则已经注册了原始的BeanDefinition,返回的是代理的BeanDifinition。
  2. 注册BeanDefinition。

3.3 ClassPathScanningCandidateComponentProvider#scanCandidateComponents

如何扫描资源,并且生成BeanDefinition的?
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
	Set<BeanDefinition> candidates = new LinkedHashSet<>();
	try {
		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);
			}
			if (resource.isReadable()) {
				try {
					//获取元数据读取器,MetadataReader 负责读取资源里面的字节码内容
					MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
					//通过ComponentScan的过滤器,去扫描当前资源是否匹配。
					if (isCandidateComponent(metadataReader)) {
						ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
						sbd.setResource(resource);
						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 (Throwable ex) {
					throw new BeanDefinitionStoreException(
							"Failed to read candidate component class: " + resource, ex);
				}
			}
			else {
				if (traceEnabled) {
					logger.trace("Ignored because not readable: " + resource);
				}
			}
		}
	}
	catch (IOException ex) {
		throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
	}
	return candidates;
}

1.由上述可知,componentScan解析器配置了三个默认的includeFilter。所以,有Component注解的class会被扫描到,并且生成BeanDefinition。
2. 获取到当前包下面所有的资源,通过includeFilter和excludeFilter判断是否为候选的spring组件。如果是,就会生成BeanDefinition。其中excludeFilter的优先级高。
3. 最终返回所有获选的BeanDefinition。BeanDefinition中存放了元数据读取器MetadataReader,元数据读取器负责读取资源中类的信息,注解、字段、方法等。

3.4 ClassPathScanningCandidateComponentProvider#isCandidateComponent

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

匹配规则如下

  1. 优先匹配excludeFilter,再匹配includeFilter。
  2. 只要有一个excludeFilter匹配到,就不会添加到候选的BeanDefinition中。
  3. includeFilter匹配到了才会添加到候选的BeanDefinition,否则为非候选。

3.5 @ComponentScan源码总结

  1. ClassPathBeanDefinitionScanner对负责扫描classPath下面的资源文件。扫描的规则是通过includeFilters和excludeFilters完成的,其中注册了三个默认的注解过滤器@Component、@ManagedBean、@Named。
  2. 元数据读取器MetadataReader:有了资源文件,那么也需要一个元数据读取器去读取资源中的内容,比如判断是否有@Component注解,这个功能就是由元数据读取器完成的。
  3. 最终将资源文件和可读取资源文件的MetadataReader封装BeanDefinition,注册到了DefaultListableBeanFactory#beanDefinitionMap。

4. TODO 其他注解

其他注解后面在写,先写完主流程。

【Spring源码】9. 超级重要的ConfigurationClassPostProcessor 上一篇我们捋了捋invokeBeanFactoryPostProcessors()的整体逻辑,由于ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor所以他会在invokeBeanFactoryPostProcessors()方法中被处理,我们debug到invokeBeanFactoryPostProcessors()方法中......... 阅读详情

相关推荐

STM32 | DMA配置和使用如此简单(超详细)

DMA学习很难吗?其实DMA配置和使用如此简单!本文让你无论在DMA认识和理解上(抽象不抽象都有)还是在DMA配置和使用上有个彻彻底底的认识。文章包括寄存器和库函数讲解,省去你自行翻阅使用手册和固件库手册,点到点教会你DMA的配置和使用。

。。。 7万+

Spring源码(六):ConfigurationClassPostProcessor详解

ConfigurationClassPostProcessor详解 本篇主要详细说明ConfigurationClassPostProcessor。 目前都是通过纯注解的方式使用Spring框架,虽然侵入性很强,但是能减少开发人员的工作量。基于注解的方式用AnnotationConfigApplicationContext上下文对象。 进入this,来到AnnotatedBeanDefinitionReader,可以看到registerAnnotationConfigProcessors中注册了Confi

JustPlayCode的博客 7029

Ollama部署本地大模型详细教程

创建ModelfilePARAMETER num_gpu 50 # GPU 层卸载比例(0~100)场景是否推荐本地开发/测试 Agent✅ 强烈推荐内部知识库问答(RAG)✅ 推荐(配合 Pgvector)生产环境高并发服务⚠️ 谨慎(建议评估 vLLM / Triton)需要 Function Calling 的 Agent❌ 不推荐(改用 DashScope API)🔑核心价值Ollama 是“本地大模型的 Docker”——让开源模型像容器一样 easy to run。

我们的征途是星辰大海 2610

【spring学习笔记 七】ConfigurationClassPostProcessor详解

文章目录概览ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistry()parse()@Component@PropertySource@ ComponentScan,@ComponentScans@Import实现ImportSelector实现ImportBeanDefinitionRegistrar接口@Beanload...

唐芬奇的博客 2228

CAD制图

CAD制图可以提高设计效率和准确性,并方便文档的存档和交流,是现代工程设计中不可或缺的一部分。·CAD三维开发是指使用计算机程序和工具来创建、编辑和操作三维模型、图形和设计的过程。它在工程、建筑、制造和游戏等领域广泛应用。CAD三维开发可能涉及到复杂的图形算法、性能优化、并行计算等方面的内容,因此需要深入学习和实践,以提高开发的效率和质量。

RZer的博客 1644

别再手动建模了!用Simscape Multibody Link一键导入SolidWorks装配体到Simulink

本文介绍了如何利用Simscape Multibody Link插件将SolidWorks装配体一键导入Simulink,实现CAD到仿真的无缝衔接。该技术显著提升机械系统设计与控制效率,特别适用于工业机器人、车辆悬架等复杂机构的动力学分析。文章详细解析了插件安装、模型导出优化及仿真验证的全流程,帮助工程师摆脱手动建模的低效工作模式。

weixin_27256197的博客 457

No bean named ‘org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration': Initialization of bean failed; nested exception is org.springframework.beans.f

weixin_43778043的博客 1万+

Spark on Yarn 高可用模式部署流程

本文介绍了如何配置Spark和Yarn的高可用(HA)环境,以确保在大规模数据处理中避免单点故障。

xhaoDream的博客 1444

ConfigurationClassPostProcessor —— Spring中最!最!最!重要的后置处理器!没有之一!!!

0. 疑惑 在阅读本文之前,可以先思考一下以下几个问题。 (1) @Configuration注解的作用是什么,Spring是如何解析加了@Configuration注解的类? (2) Spring在什么时候对@ComponentScan、@ComponentScans注解进行了解析? (3) Spring什么时候解析了@Import注解,如何解析的? (4) Spring什么...

z69183787的专栏 1670

从零搭建企业级AI网关:基于LiteLLM的安全防护体系设计

本文详细介绍了如何从零搭建企业级AI网关,基于LiteLLM构建全面的安全防护体系。内容涵盖分层防御架构、敏感数据处理、合规性配置与审计追踪,以及生产环境部署策略,助力企业安全高效地集成LLM技术,确保数据安全和合规性。

qq_27496129的博客 1291

YOLO模型部署RK3588多核NPU并行加速完整教程:基于线程池的高性能AI推理实现

本教程详细介绍了如何在RK3588平台上实现多核NPU并行加速的AI推理方案。RK3588搭载3个独立NPU核心,总算力达6TOPS,支持单核、双核和三核三种工作模式。文章深入解析了NPU硬件架构特点,包括核心独立处理能力、内存带宽分配及多核心协作机制,并提供了C++代码示例展示核心负载均衡、任务调度和内存优化策略。通过线程池技术可以有效利用多核NPU并行计算能力,显著提升AI推理性能,特别适合高并发场景。教程还包含内存子系统的瓶颈分析和性能预估方法,为开发者提供完整的优化思路。

LXY_LOVE36的博客 794

mybatis添加分页配置时报错 No bean named ‘org.springframework...ConfigurationClassPostProcessor.importRegistry

No bean named ‘org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry

u013433988的博客 2266

Android功耗系列专题理论之一:功耗优化分析思路简介

本文系统介绍了电子系统功耗分析方法,涵盖静态与动态功耗原理、电流波形分析技术和模块化拆解策略。重点阐述了PMIC与CPU协同管理机制、DVFS动态调节技术,以及通过示波器捕获波形特征定位异常的方法。针对亮屏等典型场景,提出三阶段对比分析法,并详细说明硬件/软件拆解方案及混合验证策略。文章还总结了常见问题模块(CPU、多媒体、外设)的排查流程,强调需结合日志分析、温度控制及重复测试确保数据可靠性,为功耗优化提供系统化解决方案。

1458

Spring之ConfigurationClassPostProcessor解析流程

Spring是如何解析@ComponentScan @ComponentScans @PropertySource @PropertySources @Import等注解以及相关接口 父类

好记性不如烂笔头 1867

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

深入解析ConfigurationClassPostProcessor:配置类处理器的工作机制

ConfigurationClassPostProcessor是Spring框架中用于处理Java配置类的关键组件。本文将深入解析ConfigurationClassPostProcessor的源码,详细介绍其工作原理和实现细节。我们将从其基本概念和作用出发,逐步剖析ConfigurationClassPostProcessor的初始化、配置类解析、Bean定义注册等核心步骤。

学而时习之,不亦说乎?温故而知新,可以为师矣! 9511

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Spring 中 ConfigurationClassPostProcessor 类扫描解析之 @ComponentScan 解析

读取所有资源文件判断哪些有 @Component 修饰,封装成 BeanDefinition,然后注册到 Spring 容器中完事。

龚小帅的博客 1124

错误记录No bean named ‘org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegi

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Mar

SUNforeverSUN的博客 2881

ConfigurationClassPostProcessor浅析

记录ConfigurationClassPostProcessor如何被创建以及干了哪些工作。

m13012606980的专栏 1078

ConfigurationClassPostProcessor原理详解

从spring官方介绍中,我们可以了解到ConfigurationClassPostProcessor是一个BeanFactoryPostProcessor, 用于处理一些配置信息和注解扫描,并且该处理器执行的时候有优先级顺序,优先执行PriorityOrdered,然后执行Ordered,最后执行默认没有优先级的处理器。

叔牙的专栏 1639

玩转 Spring 之 配置类解析ConfigurationClassPostProcessor

ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessors。 关于BeanDefinitionRegistryPostProcessors定义如下: public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { void postProcessBeanDefinitionRegistry(BeanDefinit

leofee的博客 973

Spring 核心类 ConfigurationClassPostProcessor 流程讲解及源码全面分析

该篇文章介绍了 Spring 核心类 ConfigurationClassPostProcessor 一些重要的方法,方法相关的源码也分析完毕,其中涉及到注解的解析,其实只要搞懂了 `processConfigBeanDefinitions` 方法里面的功能,自动装配的实现理解基本是就可以拿下了;同时,里面有针对性对每个 @Configuration 标注的配置类作了额外处理,是否进行了动态代理增强,来确保被 @Configuration 注解修饰的类下所有 @Bean 方法是否保持单例

vnjohn 博主 > 主线分享后端领域业务设计知识、源码思想、架构设计 3204

SpringBoot配置类解析-ConfigurationClassPostProcessor后置处理器

ConfigurationClassPostProcessor是一个BeanFactory的后置处理器,因此它的主要功能是参与BeanFactory的建造,在这个类中,会解析加了@Configuration的配置类,还会解析@ComponentScan、@ComponentScans注解扫描的包,以及解析@Import等注解。 ConfigurationClassPostProcessor主要重写了BeanDefinitionRegistryPostProcessor的postProcessBeanDe

BushRo 2778

SpringBoot: No bean named ‘org.springframework.context.annotation.ConfigurationClassPostProcessor.im

application.properties没配置好

weixin_42284286的博客 1612

报错:No bean named org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegis

No bean named org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry

m0_60140235的博客 5972

ConfigurationClassPostProcessor分析

ConfigurationClassPostProcessor分析

qq_42372935的博客 557
上一篇: SpringBoot 源码解析4:refresh 方法解析
下一篇: SpringBoot 基础概念:注册BeanDefinition
hao9e
博客等级 码龄7年 189粉丝 51原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值