ConfigurationClassPostProcessor

警告
本文最后更新于 2020-11-29,文中内容可能已过时。

spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework

链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。


1 主流程

在调用 invokeBeanDefinitionRegistryPostProcessors() 是,会调用到这个实现类中,来到源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
		PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		// 获取到所有的BeanDefinitionNames
		String[] candidateNames = registry.getBeanDefinitionNames();
		// 循环调用
		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			// 省略无关代码...
			//检查给定的bean定义是否适合配置类(或在配置/组件类中声明的嵌套组件类,也要自动注册),并进行相应标记。
			if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}
        // 省略无关代码...
		// Parse each @Configuration class
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);
		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			// TODO 重要程度:5 ConfigurationClassParser的parse方法调用,这里解析了一系列的注解
			parser.parse(candidates);
			parser.validate();
			Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);
			// Read the model and create bean definitions based on its content
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			// 这里装载了BeanDefinition
			this.reader.loadBeanDefinitions(configClasses);
			// 省略无关代码...
		}
		while (!candidates.isEmpty());
        // 省略无关代码...
	}
}
划重点(上面源码中的高亮部分):

  1. 校验是否符合条件,完成对@Configuration、@Component、@ComponentScan、@Import、@ImportResource、方法上包含@Bean 的支持。
  2. 执行 parse(),完成解析。
  3. loadBeanDefinitions(),装载 BeanDefinition。

2 循环所有benaName、校验bean是否符合

进入 checkConfigurationClassCandidate() 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
abstract class ConfigurationClassUtils {
    public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
        // 省略无关代码...
		// 类上是否包含@Configuration
		if (isFullConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		// 类上是包含@Component、@ComponentScan、@Import、@ImportResource。方法上包含@Bean
		else if (isLiteConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
		else {
			return false;
		}
        // 省略无关代码
		return true;
	}
}

isFullConfigurationCandidate() 源码:

1
2
3
4
5
abstract class ConfigurationClassUtils {
    public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
		return metadata.isAnnotated(Configuration.class.getName());
	}
}

isLiteConfigurationCandidate() 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
abstract class ConfigurationClassUtils {
    // 注意这个成员变量,包含了对一些内置注解的支持。
   	private static final Set<String> candidateIndicators = new HashSet<>(8);
   
   	static {
   		// 添加了一系列注解的支持。
   		candidateIndicators.add(Component.class.getName());
   		candidateIndicators.add(ComponentScan.class.getName());
   		candidateIndicators.add(Import.class.getName());
   		candidateIndicators.add(ImportResource.class.getName());
   	}
    public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
		// 类上是包含@Component、@ComponentScan、@Import、@ImportResource。方法上包含@Bean
		for (String indicator : candidateIndicators) {
			if (metadata.isAnnotated(indicator)) {
				return true;
			}
		}
        // @Bean的支持
        return metadata.hasAnnotatedMethods(Bean.class.getName());
	}
}

3 解析

来到 parse() 源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class ConfigurationClassParser {
	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			//这是哪个分支的解析会走到同一个方法里
            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 {
                // TODO 执行解析,记着这个方法,这里在满足条件时会被递归调用。
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
		}
		this.deferredImportSelectorHandler.process();
	}
}

对不同类型的 BeanDefinition 会调用不同的重载 parse(),实际上,最终都会进到这个方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class ConfigurationClassParser {
	protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
		// 省略无关代码...
		// Recursively process the configuration class and its superclass hierarchy.
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			// 执行解析
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);

		// 这里将配置类的信息放入缓存中,等解析完之后,会将配置类进行LoadBeanDefinitions();
		this.configurationClasses.put(configClass, configClass);
	}
}

是在一个循环中调用的,如果返回不为空,就一直执行。每次会更新sourceClass的值。

进入 doProcessConfigurationClass():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
class ConfigurationClassParser {
    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {
		// Component注解支持
		if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
			// Recursively process any member (nested) classes first
			processMemberClasses(configClass, sourceClass);
		}

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		// @ComponentScan,@ComponentScans支持
		// 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) {
				// 使用@ComponentScan注释配置类->立即执行扫描
				// 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();
					}
					// TODO 如果是配置类,则递归调用ConfigurationClassParser.parse()
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// 处理@Import注解导入
		// Process any @Import annotations
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		// 处理@ImportResource注解导入
		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

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

		// 处理接口上的默认方法
		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}
}

以上代码完成了对相关注解的扫描解析。这里着重看一下 ComponentScan 的解析,其他的注解解析,源码中有注释。

ComponentScan的解析,大概分为这么几步:

  1. 将扫描到的类封装成 BeanDefinitionHolder,放入一个 Set 集合中。(在扫描方法里已经完成了 BeanDefinition 的注册)
  2. 调用 checkConfigurationClassCandidate(),这个方法具体怎么检查的,前面有解释
  3. 如果 check 的结果为true,说明也是个配置类,递归调用 parse() 方法。

关于 componentScanParser.parse() 的源码分析,在 Spring标签解析-BeanDefinitionParser 中有说明。

来到 processImports(),看一下是如何处理@Import注解的。首先来到getImports():

1
2
3
4
5
6
7
8
9
class ConfigurationClassParser {
	private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
		Set<SourceClass> imports = new LinkedHashSet<>();
		Set<SourceClass> visited = new LinkedHashSet<>();
		// 收集@import注解。
		collectImports(sourceClass, imports, visited);
		return imports;
	}
}

继续往下跟踪代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class ConfigurationClassParser {
	private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
			throws IOException {
		// 获取所有的注解,遍历判断注解是否是@Import,将其搜集起来
		if (visited.add(sourceClass)) {
			for (SourceClass annotation : sourceClass.getAnnotations()) {
				String annName = annotation.getMetadata().getClassName();
				if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
					collectImports(annotation, imports, visited);
				}
			}
			imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
		}
	}
}

从上面源码中可以看到,这里获取了所有的注解,然后遍历寻找@Import注解,将其搜集起来。

然后进入 processImports(),会将上面流程中搜集到的@Import注解,作为参数传递进来:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class ConfigurationClassParser {
	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

		if (importCandidates.isEmpty()) {
			return;
		}
		// 检测循环导入
		if (checkForCircularImports && isChainedImportOnStack(configClass)) {
			this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
		}
		else {
			// 这里是一个ArrayDeque
			this.importStack.push(configClass);
			
            for (SourceClass candidate : importCandidates) {
                // 这里会处理ImportSelector类型的。
                if (candidate.isAssignable(ImportSelector.class)) {
                    // Candidate class is an ImportSelector -> delegate to it to determine imports
                    Class<?> candidateClass = candidate.loadClass();
                    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
                    ParserStrategyUtils.invokeAwareMethods(
                            selector, this.environment, this.resourceLoader, this.registry);
                    if (selector instanceof DeferredImportSelector) {
                        this.deferredImportSelectorHandler.handle(
                                configClass, (DeferredImportSelector) selector);
                    }
                    else {
                        // 实例化 ImportSelector 后,会执行selectImports()方法。获取到一个className数组。
                        // 将其封装为SourceClass,然后递归调用当前方法,最终会走到else语句块中。
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                        processImports(configClass, currentSourceClass, importSourceClasses, false);
                    }
                }
                // EnableAspectJAutoProxy中导入的AspectJAutoProxyRegistrar就是实现了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 =
                            BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
                    ParserStrategyUtils.invokeAwareMethods(
                            registrar, 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));
                }
            }
		}
			// 省略...
	}
}

上面代码分为三个分支:

  • 遍历所有的importCandidates,判断是否为ImportSelector,如果为true,则执行selectImports(),获取到导入的className数组。然后递归调用,最终会执行到步骤3。
  • 处理ImportBeanDefinitionRegistrar类型的,与步骤1类似,然后直接注册。跳出方法
  • 将 Metadata 存储到 importStack 中,然后递归调用 processConfigurationClass,这也是一个@Configuration类。

4 装载BeanDefinition

loadBeanDefinitions() 方法参数是一个 ConfigurationClass 的集合,从名字就可以看出,这是一个配置类的集合。

这些配置类是在上面的流程中,扫描包中的类时,扫描到有配置信息的类。

最终会进行以下调用,又会回到 springioc 容器初始化时,解析标签的那段代码。

关键源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class ConfigurationClassBeanDefinitionReader {
	private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
        // 省略无关代码...
		// @Import注解中,导入的类,包装为BeanDefinition并注册到容器。
        if (configClass.isImported()) {
            registerBeanDefinitionForImportedConfigurationClass(configClass);
        }
        // @Bean注解的方法,将对应的metadata包装为BeanDefinition并注册到容器。
        // 这里并没有执行方法,真正执行方法是在实例化的时候。
        for (BeanMethod beanMethod : configClass.getBeanMethods()) {
            loadBeanDefinitionsForBeanMethod(beanMethod);
        }

        // 加载导入资源
        loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
        // 这里是加载ImportBeanDefinitionRegistrar接口注册的BeanDefinition,通过调用registerBeanDefinitions()来注入
        loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}
}

从源码注释中可以很清楚的看到,loadBeanDefinitions() 都做了那些事情。

  1. 解析@Import 导入的类,包装为 BeanDefinition 并注册到容器。
  2. 解析 @Bean 注解的方法,包装成 BeanDefinition 并注册到容器。
  3. 加载导入资源的BeanDefinition,这个是import标签的支持。
  4. 调用容器中已注册的 ImportBeanDefinitionRegistrar 实现类的 registerBeanDefinitions()。
技巧
AOP的支持,就是通过 ImportBeanDefinitionRegistrar.registerBeanDefinitions() 注册进来的,其实现类是 AspectJAutoProxyRegistrar。

调用链流程如下:

graph TB;
    A(loadBeanDefinitions) --> |forEach| B(configClass)
    B --> B2(loadBeanDefinitionsForConfigurationClass)
    B2 --> C{分别调用}
    C -->|One| D(loadBeanDefinitionsFromImportedResources) 
    C -->|Two| E(loadBeanDefinitionsFromRegistrars)
    D --> D-1(loadBeanDefinitions)
    D-1 --> D-2(doLoadBeanDefinitions)
    D-2 --> D-3(registerBeanDefinitions)
    D-3 --> D-4(doRegisterBeanDefinitions)
    D-4 --> D-5(parseBeanDefinitions)
    D-4 --> D-5-1(parseDefaultElement)    
    E --> E-1(registerBeanDefinitions)