前言
这一小节,是学习 Spring Boot 配置类的处理。
这里面细节还是挺多的,越看越感觉自己功力还不够,需要多多加油。
还是先记录一个大概的处理流程。后面再继续探究。
配置类解析
配置类解析入口
配置类解析的入口,在 refresh()
方法的中invokeBeanFactoryPostProcessors(beanFactory)
:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// .....
}
}
复制代码
继续进入到PostProcessorRegistrationDelegate # invokeBeanFactoryPostProcessors()
方法中,有这样一段代码:
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
// 常规后处理器集合初始化
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
// 注册处理器集合初始化
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 遍历所有处理器
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
// 如果实现了 BeanDefinitionRegistryPostProcessor 进行注册 BeanDefinition 的处理
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
......
}
复制代码
这里,对 BeanDefinitionRegistryPostProcessor
的实现,做了特殊的逻辑处理,其中有一个实现为ConfigurationClassPostProcessor
:
BeanFactoryPostProcessor 用于引导被
@Configuration
注解的类的处理。
这个后处理器是优先处理的。因为在任何其他BeanFactoryPostProcessor执行之前, 被@Configuration注解的类中声明的,任何被 @Bean 注解的方法,都必须注册其相应的 bean 定义,这一点很重要。
我们进入ConfigurationClassPostProcessor
被调用的方法中:
@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);
}
复制代码
processConfigBeanDefinitions
继续进入到此方法中:
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) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
......
}
// 判断是否是配置类。如果是,设置 BeanDefiniton 的属性lite/full。
// @Configuration --> full
// @Bean、@Component、@ComponentScan、@Import、@ImportResource --> lite
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;
}
......
// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
......
}
}
......
// Parse each @Configuration class
// 解析每个@Configuration 类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// BeanDefiniton候选集合
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
// 已解析 BeanDefiniton 集合
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 解析 配置类中的 @Component、@PropertySources、@ComponentScans、@ImportResource、@Bean。
// 只有 @Configuration 与 @ComponetScans 会被加载为 BeanDefinition
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());
}
// parser.parse(candidates)解析出来的这些bean可能会引入新的bean,例如实现了ImportBeanDefinitionRegistrar或者ImportSelector接口的bean,或者 bean中存在被@Bean注解的方法。
// 所以执行一次loadBeanDefinition()。用于解析 ImportBeanDefinitionRegistrar 或 ImportSelector接口 或@Bean 注释的方法。
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
.......
}
复制代码
parser.parse(candidates)
这个方法是,解析配置类的核心方法。
// 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());
}
}
......
}
复制代码
进入重载方法:
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName));
}
复制代码
processConfigurationClass():
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
// 判断是否符合解析条件
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
......
// Recursively process the configuration class and its superclass hierarchy.
// 递归处理配置类及其超类层次结构。
// 等于说 doProcessConfigurationClass 是处理加载 Configuration 的核心递归逻辑。
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
复制代码
doProcessConfigurationClass():
通过从源类中读取注解、成员和方法来应用处理并构建一个完整的ConfigurationClass 。
好!从这里开始,我们可以想想,一个 Configuration 类被开始解析了。
下面我们来看一下解析步骤:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
// 1. 首先处理成员变量。如果成员里有配置类,就会 processConfigurationClass。如此形成了递归。
processMemberClasses(configClass, sourceClass);
}
// Process any @PropertySource annotations
// 2. 处理 @PropertySource,主要是加载属性到环境中
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
....
}
// Process any @ComponentScan annotations
// 3. 处理 @ComponentScan 注解。扫描配置类上配置的扫描路径中的 Bean。
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
// 扫描 @ComponentScan
// 这个方法去扫描配置的扫描范围。其实最开始时,加载的是我们的引导类。然后去扫描了引导类所在包的全部子包。
// 然后把我们写的其他 Configuration 才被扫描到,加载进来。
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();
}
// 如果扫描出来的是 Configuration ,就递归调用解析
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
// 4. 处理@Import注解。通过@Import注解,引入的配置类。
// 这里也需要注意,后面的自动装配就是通过@Import作为入口的。
processImports(configClass, sourceClass, getImports(sourceClass), true);
// Process any @ImportResource annotations
// 5. 处理@ImportResource注解,解析配置文件。
// 这里需要注意,处理方式是 addImportedResource。也就是只赋值给了 configClass 的属性,并没有做其他处理。
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
// 6. 处理被 @Bean 注释的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
// 7. 处理接口的默认方法
processInterfaces(configClass, sourceClass);
// Process superclass, if any
// 8. 类,类包名以java开头的除外
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;
}
复制代码
这个方法是,解析 配置类的核心方法,其中解析了@Component、@PropertySources、@ComponentScans、@ImportResource、@Bean,还解析了java8以后提供的默认方法。
这些个方法就不一个个细看了,第一次看源码,还是先把大概流程看明白叭。
总结
这一节,大致梳理了 Configuration 类的主干处理流程。
还有很多细节,留待学习。