你好,我是柳岸花开。
大家好,今天我们来深入探讨一下Spring框架中的配置类解析与扫描过程的源码。Spring作为Java开发中最为广泛使用的框架之一,其核心机制一直是开发者关注的焦点。本文将带领大家从源码角度,详细剖析Spring配置类的解析与扫描过程。
配置类解析概述
在Spring中,配置类主要是通过注解的方式进行定义和解析的。其中,@Configuration
和@ComponentScan
是两个最为重要的注解。
-
@Configuration
:标识一个类为配置类,类似于传统的XML配置文件。 -
@ComponentScan
:用于指定Spring在初始化时需要扫描的包路径,从而找到标注了特定注解的类。
这些注解的解析过程是通过Spring的内部机制来实现的,下面我们将一步步解析这个过程。
源码分析
1. @Configuration
注解的解析
Spring在启动时,会通过ConfigurationClassPostProcessor
类对所有标注了@Configuration
注解的类进行处理。其核心方法是processConfigBeanDefinitions
,该方法的主要工作是扫描所有的bean定义,并找出配置类进行处理。
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 后续处理逻辑
}
在这个过程中,Spring会识别所有的配置类,并进行后续的处理。
2. @ComponentScan
注解的解析
配置类被识别后,Spring会继续解析其中的@ComponentScan
注解。这个注解的解析逻辑在ClassPathBeanDefinitionScanner
类中实现。其核心方法是doScan
,该方法会扫描指定包路径下的所有类,并将符合条件的类注册为Bean。
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
beanDefinitions.add(new BeanDefinitionHolder(candidate, generateBeanName(candidate)));
}
}
return beanDefinitions;
}
该方法会递归扫描指定的包路径,并筛选出符合条件的类,注册为Spring的Bean。
解析配置具体步骤
-
在启动Spring时,需要传入一个AppConfig.class给ApplicationContext,ApplicationContext会根据AppConfig类封装为一个BeanDefinition,这种BeanDefinition我们把它称为配置类BeanDefinition。 -
ConfigurationClassPostProcessor中会把配置类BeanDefinition取出来 -
构造一个ConfigurationClassParser用来解析配置类BeanDefinition,并且会生成一个配置类对象ConfigurationClass -
如果配置类上存在@Component注解,那么 解析配置类中的内部类(这里有递归,如果内部类也是配置类的话) -
如果配置类上存在@PropertySource注解,那么则解析该注解,并得到PropertySource对象,并添加到environment中去 -
如果配置类上存在@ComponentScan注解,那么则解析该注解,进行扫描,扫描得到一系列的BeanDefinition对象,然后判断这些BeanDefinition是不是也是配置类BeanDefinition(只要存在@Component注解就是配置类,所以基本上扫描出来的都是配置类),如果是则继续解析该配置类, (也有递归),并且会生成对应的ConfigurationClass -
如果配置类上存在@Import注解,那么则判断Import的类的类型: -
如果是ImportSelector,那么调用执行selectImports方法得到类名,然后在把这个类当做配置类进行解析 (也是递归) -
如果是ImportBeanDefinitionRegistrar,那么则生成一个ImportBeanDefinitionRegistrar实例对象,并添加到配置类对象中(ConfigurationClass)的 importBeanDefinitionRegistrars属性中。
-
-
如果配置类上存在@ImportResource注解,那么则把导入进来的资源路径存在配置类对象中的 importedResources属性中。 -
如果配置类中存在@Bean的方法,那么则把这些方法封装为BeanMethod对象,并添加到配置类对象中的 beanMethods属性中。 -
如果配置类实现了某些接口,则看这些接口内是否定义了@Bean的默认方法 -
如果配置类有父类,则把父类当做配置类进行解析 -
AppConfig这个配置类会对应一个ConfigurationClass,同时在解析的过程中也会生成另外的一些ConfigurationClass,接下来就利用reader来进一步解析ConfigurationClass -
如果ConfigurationClass是通过@Import注解导入进来的,则把这个类生成一个BeanDefinition,同时解析这个类上@Scope,@Lazy等注解信息,并注册BeanDefinition -
如果ConfigurationClass中存在一些BeanMethod,也就是定义了一些@Bean,那么则解析这些@Bean,并生成对应的BeanDefinition,并注册 -
如果ConfigurationClass中导入了一些资源文件,比如xx.xml,那么则解析这些xx.xml文件,得到并注册BeanDefinition -
如果ConfigurationClass中导入了一些ImportBeanDefinitionRegistrar,那么则执行对应的registerBeanDefinitions进行BeanDefinition的注册
-
👇关注我,下期了解👇
Spring之整合Mybatis底层源码解析
回复 222,获取Java面试题合集
关于我
一枚爱折腾的Java程序猿,专注Spring干货。把路上的问题记录下来,帮助那些和我一样的人。
好奇心强,喜欢并深入研究古天文。
崇尚 个人系统创建,做一些时间越长越有价值的事情。思考 把时间留下来 又 每刻都是新的。
本文由 mdnice 多平台发布