网站导航条怎么做效果,绿色网站配色,建设网站需要注意什么,简单的企业网站制作tips#xff1a; ConfigurationClassParser 是 Springframework 中的重要类。
本章主要是源码理解#xff0c;有难度和深度#xff0c;也枯燥乏味#xff0c;可以根据实际情况选择阅读。
位置#xff1a;org.springframework.context.annotation.ConfigurationClassPars… tips ConfigurationClassParser 是 Springframework 中的重要类。
本章主要是源码理解有难度和深度也枯燥乏味可以根据实际情况选择阅读。
位置org.springframework.context.annotation.ConfigurationClassParser
ConfigurationClassParser 它是解密 configuration 的关键。理解 ConfigurationClassParser 对理解整个 Spring 框架至关重要。 一、作用是什么
ConfigurationClassParser是一个非常重要的类它主要用于解析带有Configuration注解的类。
Configuration注解表明该类用作配置类其中可以定义bean和Spring容器应如何初始化和管理这些bean。
ConfigurationClassParser的作用可以从以下几个方面详细阐述 解析导入的配置Import注解允许一个配置类导入另一个配置类。ConfigurationClassParser解析这些Import注解确保所有导入的配置也被处理和应用。处理属性注入通过PropertySource注解可以指定一些属性文件这些属性文件中的属性可以被注入到Spring管理的bean中。ConfigurationClassParser负责解析这些注解并确保属性文件被加载且其值可用于注入。处理Conditional注解: Spring框架允许在bean的注册过程中使用条件逻辑Conditional注解及其派生注解例如ConditionalOnClassConditionalOnProperty等使得只有在满足特定条件时才会进行bean的注册。ConfigurationClassParser负责解析这些条件注解并应用其逻辑。processDeferredImportSelectors#processImports 处理扩展配置( Starter 能够被处理的核心分支)
二、触发时机
SpringBoot 应用启动过程中通过后置处理器去触发 ConfigurationClassPostProcessor。 然后再调用 ConfigurationClassParser类解析
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {....
} 处理如下 下面我们将详细分析源码流程。
三、ConfigurationClassPostProcessor
下面是 ConfigurationClassPostProcessor 部分核心代码。
入口方法 processConfigBeanDefinitions(BeanDefinitionRegistry registry) 。开始分析这段代码。 注意这里有一个 do...while do {.....}while (!candidates.isEmpty());
它将逐一识别和解析配置类然后将配置类中定义的Bean注册到Spring容器中。这个过程通过不断循环直到没有新的配置类候选者出现为止确保了所有相关的配置都被完整地处理。
// 创建一个配置类解析器用于解析和处理配置类信息
ConfigurationClassParser parser new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);// 初始化一个集合用于存储待处理的配置类候选者
SetBeanDefinitionHolder candidates new LinkedHashSet(configCandidates);
// 初始化一个集合用于跟踪已经解析过的配置类以避免重复解析
SetConfigurationClass alreadyParsed new HashSet(configCandidates.size());// 循环处理直到没有新的配置类候选者
do {// 解析当前候选者中的配置类parser.parse(candidates);// 对解析结果进行验证parser.validate();// 从解析器中获取已解析的配置类集合SetConfigurationClass configClasses new LinkedHashSet(parser.getConfigurationClasses());// 移除已经处理过的避免重复处理configClasses.removeAll(alreadyParsed);// 如果读取器未初始化创建一个配置类Bean定义读取器if (this.reader null) {this.reader new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}// 加载并注册配置类中定义的Beanthis.reader.loadBeanDefinitions(configClasses);// 将这批配置类标记为“已解析”alreadyParsed.addAll(configClasses);// 清空候选者集合为下一轮寻找新候选者做准备candidates.clear();// 检查是否有新的Bean定义被注册可能由Configuration类引入if (registry.getBeanDefinitionCount() candidateNames.length) {// 重新获取所有Bean定义的名称String[] newCandidateNames registry.getBeanDefinitionNames();// 创建一个旧候选名称的集合用于辨识新的候选者SetString oldCandidateNames new HashSet(Arrays.asList(candidateNames));// 创建一个集合用于跟踪已经解析的配置类的类名SetString alreadyParsedClasses new HashSet();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}// 遍历新的Bean定义名称寻找新的配置类候选者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));}}}// 更新候选名称列表以反映新的Bean定义candidateNames newCandidateNames;}
// 如果还有未处理的候选者继续循环
} while (!candidates.isEmpty()); 特别说明对于 Bean 的加载和实例化不在本范围了不进行讲解。感兴趣可以阅读相关章节。
上面的这段代码是 ConfigurationClassPostProcessor 核心。解析来的重头戏。ConfigurationClassParser
四、ConfigurationClassParser
从这行代码开始入手parser.parse(candidates); parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());processDeferredImportSelectors(); // 处理前面推迟的ImportSelector一些二方包的导入类将在这个方法中实现。 例如我们配置在 Starter Spring.factories 中的自动导入类将在这一环境被加载
parse方法入口
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {// 如果这个配置类应该根据条件注解被跳过则直接返回不进行处理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);}// 如果已存在的配置类不是导入的则忽略当前导入的配置类保留现有的非导入类return;}else {// 如果找到显式的bean定义可能是意在替换一个导入的类。// 移除旧的配置类采用新的配置类。this.configurationClasses.remove(configClass);this.knownSuperclasses.values().removeIf(configClass::equals);}}// 递归处理配置类及其超类层次结构SourceClass sourceClass asSourceClass(configClass);do {// 处理当前配置类并更新sourceClass为配置类的超类准备下一轮处理sourceClass doProcessConfigurationClass(configClass, sourceClass);} while (sourceClass ! null); // 如果sourceClass为null表示超类已经处理完毕// 将处理完的配置类放入配置类映射中标记为已处理this.configurationClasses.put(configClass, configClass);
}上面可以理解解析 MyApplication 类所在工程中的类。最终的解析由 doProcessConfigurationClass 实现
processDeferredImportSelectors
负责处理那些被延迟的特殊接口使用它来按需动态地导入配置。
这些常常依赖于某些条件才被执行所以被延迟处理。
// 定义处理延迟的ImportSelector的方法
private void processDeferredImportSelectors() {// 获取之前收集的所有延迟处理的ImportSelectorListDeferredImportSelectorHolder deferredImports this.deferredImportSelectors;// 将引用置为null表示开始处理过程防止重复处理this.deferredImportSelectors null;// 如果没有需要处理的延迟ImportSelector则直接返回if (deferredImports null) {return;}...... // 遍历所有的延迟ImportSelectorfor (DeferredImportSelectorHolder deferredImport : deferredImports) {// 获取与当前ImportSelector相关联的配置类ConfigurationClass configClass deferredImport.getConfigurationClass();try {// 调用ImportSelector的selectImports方法获取所有的导入类名String[] imports deferredImport.getImportSelector().selectImports(configClass.getMetadata());// 处理这些导入的类将它们作为配置类进行进一步的处理processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);}}
}这个方法的主要作用将是找出符合条件的 imports 类。最终还是由processImports() 处理。
到这里spring.factories 符合条件的一些类将被加载。核心代码deferredImport.getImportSelector().selectImports(configClass.getMetadata()); 到这里我们基本上了解了 Starter 是如何被引入进来的。
真正解析的方法 doProcessConfigurationClass
doProcessConfigurationClass
它递归地处理嵌套类、处理PropertySource注解、处理ComponentScan注解、处理Import注解、处理ImportResource注解、处理Bean方法、处理接口上的默认方法最后处理父类 处理成员类方法首先递归地处理配置类中定义的任何成员类嵌套类。处理PropertySource注解然后遍历配置类上的所有PropertySource注解这些注解用来指明属性文件的位置。如果当前的环境实现了ConfigurableEnvironment接口则处理注解指定的属性源处理ComponentScan注解接下来处理配置类上的所有ComponentScan注解这些注解指示Spring扫描特定包下的组件即带有Component、Service等注解的类并注册为Spring容器中的Bean。如果有条件注解指示在此阶段跳过处理则不执行扫描。处理Import注解处理配置类上的Import注解这些注解用来导入其他配置类或配置选择器允许模块化地组织配置。处理ImportResource注解处理配置类上的ImportResource注解这些注解用于导入XML配置文件。处理Bean方法收集配置类中所有带有Bean注解的方法的元数据并将它们添加到配置类对象中。这些方法定义了应该由Spring容器管理的Bean。处理接口上的默认方法如果配置类实现了接口并在这些接口上定义了默认方法这些方法也会被处理。处理父类最后如果配置类有超类那么这个方法会检查超类是否也是一个配置类不是Java内置类并且还没有被处理过。如果满足条件则递归地处理这个超类。 protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {// 首先递归处理任何成员类嵌套类processMemberClasses(configClass, sourceClass);// 处理所有的PropertySource注解for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {// 如果环境实现了ConfigurableEnvironment接口if (this.environment instanceof ConfigurableEnvironment) {// 处理PropertySource注解processPropertySource(propertySource);} else {// 如果环境没有实现ConfigurableEnvironment接口logger.warn(忽略了[ sourceClass.getMetadata().getClassName() ]上的PropertySource注解。原因环境必须实现ConfigurableEnvironment接口);}}// 处理所有的ComponentScan注解SetAnnotationAttributes componentScans AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);// 如果存在ComponentScan注解并且当前阶段不应该跳过if (!componentScans.isEmpty() !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {// 循环处理每个ComponentScan注解for (AnnotationAttributes componentScan : componentScans) {// 配置类上存在ComponentScan注解 - 立即执行扫描SetBeanDefinitionHolder scannedBeanDefinitions this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// 检查扫描结果中的定义集合如果有进一步的配置类递归解析for (BeanDefinitionHolder holder : scannedBeanDefinitions) {if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());}}}}// 处理所有的Import注解processImports(configClass, sourceClass, getImports(sourceClass), true);// 处理所有的ImportResource注解AnnotationAttributes importResource AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);// 如果ImportResource注解存在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方法SetMethodMetadata beanMethods retrieveBeanMethodMetadata(sourceClass);// 循环处理每个Bean方法for (MethodMetadata methodMetadata : beanMethods) {// 添加Bean方法到配置类configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// 处理接口上的默认方法processInterfaces(configClass, sourceClass);// 处理父类如果存在的话if (sourceClass.getMetadata().hasSuperClass()) {// 获取父类名称String superclass sourceClass.getMetadata().getSuperClassName();// 如果父类存在并且父类不是java.*开头并且尚未处理过if (superclass ! null !superclass.startsWith(java) !this.knownSuperclasses.containsKey(superclass)) {// 记录已知的父类this.knownSuperclasses.put(superclass, configClass);// 找到父类返回其注解元数据并递归处理return sourceClass.getSuperClass();}}// 没有父类 - 处理完成return null;
}到这里大体流程我们已经清楚。不再继续针对注解的解析进行讲解感兴趣可以自行下载源码阅读理解。
五、本章小结
本章是对整 ConfigurationClassParser 进行讲解它是 Spring framework 中的最核心类。
到这里Starter 的整个过程已经分析完成但是针对条件装配我们将在下一章进行讲解。 已同步发布到公众号面汤放盐 第七节 ConfigurationClassParser 源码分析 (qq.com)
掘金账号第七节 ConfigurationClassParser 源码分析 - 掘金 (juejin.cn)