由于md文档部分hugo插件语法不兼容,建议访问作者网站查阅文章:wlizhi.cc
spring源码系列文章,示例代码的中文注释,均是 copy 自 https://gitee.com/wlizhi/spring-framework 。
链接中源码是作者从 github 下载,并以自身理解对核心流程及主要节点做了详细的中文注释。
1 主流程
在调用 invokeBeanDefinitionRegistryPostProcessors() 是,会调用到这个实现类中,来到源码:
{{< highlight java “linenos=table,hl_lines=12 25 36,linenostart=1” >}}
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List 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 candidates = new LinkedHashSet<>(configCandidates);
Set alreadyParsed = new HashSet<>(configCandidates.size());
do {
// TODO 重要程度:5 ConfigurationClassParser的parse方法调用,这里解析了一系列的注解
parser.parse(candidates);
parser.validate();
Set 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());
// 省略无关代码…
}
}
{{< /highlight >}}
划重点(上面源码中的高亮部分):
- 校验是否符合条件,完成对@Configuration、@Component、@ComponentScan、@Import、@ImportResource、方法上包含@Bean 的支持。
- 执行 parse(),完成解析。
- loadBeanDefinitions(),装载 BeanDefinition。
2 循环所有benaName、校验bean是否符合
进入 checkConfigurationClassCandidate() 源码:
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() 源码:
abstract class ConfigurationClassUtils {
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
return metadata.isAnnotated(Configuration.class.getName());
}
}
isLiteConfigurationCandidate() 源码:
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() 源码:
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(),实际上,最终都会进到这个方法:
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():
{{< highlight java “linenos=table,hl_lines=23-46,linenostart=1” >}}
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;
}
}
{{< /highlight >}}
以上代码完成了对相关注解的扫描解析。这里着重看一下 ComponentScan 的解析,其他的注解解析,源码中有注释。
ComponentScan的解析,大概分为这么几步:
- 将扫描到的类封装成 BeanDefinitionHolder,放入一个 Set 集合中。(在扫描方法里已经完成了 BeanDefinition 的注册)
- 调用 checkConfigurationClassCandidate(),这个方法具体怎么检查的,前面有解释 。
- 如果 check 的结果为true,说明也是个配置类,递归调用 parse() 方法。
关于 componentScanParser.parse() 的源码分析,在 Spring标签解析-BeanDefinitionParser 中有说明。
来到 processImports(),看一下是如何处理@Import注解的。首先来到getImports():
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;
}
}
继续往下跟踪代码:
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注解,作为参数传递进来:
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 容器初始化时,解析标签的那段代码。
关键源码:
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() 都做了那些事情。
- 解析@Import 导入的类,包装为 BeanDefinition 并注册到容器。
- 解析 @Bean 注解的方法,包装成 BeanDefinition 并注册到容器。
- 加载导入资源的BeanDefinition,这个是import标签的支持。
- 调用容器中已注册的 ImportBeanDefinitionRegistrar 实现类的 registerBeanDefinitions()。
{{< admonition type=“tip” title=“技巧” open=true >}}
AOP的支持,就是通过 ImportBeanDefinitionRegistrar.registerBeanDefinitions() 注册进来的,其实现类是 AspectJAutoProxyRegistrar。
{{< /admonition >}}
调用链流程如下:
{{< mermaid >}}
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)
{{< /mermaid >}}