一起学SF框架系列5.8-spring-Beans-Bean注解解析3-解析配置component-scan
本文主要讲述Spring是如何解析“context:component-scan”元素,扫描加载目录下的BeanDefinition。
解析内容
1、解析的元素如下:
<!-- 注解模式:配置bean扫描路径(注:自动包含子路径) --><context:component-scan base-package="com.learnsf.main,com.learnsf.service"/>
注:该元素解析过程中,会自动处理“context:annotation-config/”元素要解析的内容。
2、只扫描加载目录下的BeanDefinition,不对注解进行解析。在AbstractApplicationContext.invokeBeanFactoryPostProcessors统一解析。
解析
ComponentScanBeanDefinitionParser.parse(Element element, ParserContext parserContext)
解析元素“context:component-scan”。
@Override@Nullablepublic BeanDefinition parse(Element element, ParserContext parserContext) {// 获取属性“base-package”(多个包路径)String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);// 对包路径中占位符进行替换处理basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);// 分解成包数组String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);// 生成BeanDefinition扫描器ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);// 扫描生成beanDefinitionSet<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);// 注册BeanDefinitionregisterComponents(parserContext.getReaderContext(), beanDefinitions, element);return null;}
ClassPathBeanDefinitionScanner.doScan(String… basePackages)
在包里扫描BeanDefinition。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();// 每个包进行扫描for (String basePackage : basePackages) {// 获取候选BeanDefinitionSet<BeanDefinition> candidates = findCandidateComponents(basePackage);// 每个侯选者处理for (BeanDefinition candidate : candidates) {// 解析Bean作用域ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());// 生成beanName String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition abstractBeanDefinition) {// 对 AbstractBeanDefinition类型的BeanDefinition 进一步处理:赋值BeanDefinition属性默认值,并设置 autowireCandidate 属性postProcessBeanDefinition(abstractBeanDefinition, beanName);}if (candidate instanceof AnnotatedBeanDefinition annotatedBeanDefinition) {// 对 AnnotatedBeanDefinition 类型的 BeanDefinition 进一步处理:对通用注解的解析处理,通用注解包括 @Lazy、@Primary、@DependsOn、@Role、@Description。例如:如果当前类被@Lazy修饰,则会获取@Lazy 的value 值并保存到 BeanDefinition#lazyInit 属性中。AnnotationConfigUtils.processCommonDefinitionAnnotations(annotatedBeanDefinition);}// 检查给定候选bean的beanName,确定相应的bean定义是否需要注册或与现有定义冲突if (checkCandidate(beanName, candidate)) {// 封装候选BeanDefinition为BeanDefinitionHolder BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);// 对 BeanDefinitionHolder 填充代理信息definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);// 加入到返回集合beanDefinitions.add(definitionHolder);// 注册BeanDefinitionHolder 到bean工厂容器中registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}
ClassPathScanningCandidateComponentProvider.findCandidateComponents(String basePackage)
ClassPathScanningCandidateComponentProvider是ClassPathBeanDefinitionScanner父类。
获取注解的Bean的BeanDefinition。
public Set<BeanDefinition> findCandidateComponents(String basePackage) {if (this.componentsIndex != null && indexSupportsIncludeFilters()) {// @Indexed 注解的处理 注1return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);}else {// 非@Indexed 注解的处理return scanCandidateComponents(basePackage);}}
注1:@Indexed注解Spring在5.0版本引入的,主要解决启动时注解模式解析太长的问题。处理方式是在项目编译打包时,会自动生成META-INF/spring.components文件,文件包含被@Indexed注释的类的模式解析结果。当Spring应用上下文进行组件扫描时,META-INF/spring.components会被org.springframework.context.index.CandidateComponentsIndexLoader读取并加载,转换为CandidateComponentsIndex对象,此时组件扫描会读取CandidateComponentsIndex,而不进行实际扫描,从而提高组件扫描效率,减少应用启动时间。如果使用该功能,需要引入如下依赖:
<dependency><groupId>org.springframework</groupId><artifactId>spring-context-indexer</artifactId><version>${spring.version}</version><optional>true</optional>
</dependency>
ClassPathScanningCandidateComponentProvider.scanCandidateComponents(String basePackage)
该方法是从指定的包路径获取到字节码文件,筛选出可能注入到Spring容器的Bean生成对应的ScannedGenericBeanDefinition
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<>();try {// // 形成完整包路径String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;// 扫描路径下的资源(字节码文件)Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();// 遍历所有资源(字节码文件),挑选有注解的字节码文件生成BeanDefinitionfor (Resource resource : resources) {String filename = resource.getFilename();if (filename != null && filename.contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {// Ignore CGLIB-generated classes in the classpathcontinue;}if (traceEnabled) {logger.trace("Scanning " + resource);}try {// 获得资源的MetadataReader(包含文件信息和对应类注解信息)MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);// 校验是否是候选组件,条件是:包含在include-filters(扫描时需要实例化的类,默认都包含)且 @Conditional注解中不跳过的类(默认都不跳过)if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setSource(resource);// 校验是否是候选组件:bean是独立且具体的类 或者 是抽象类但被@Lookup注解修饰if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}// 加入返回的候选组件集candidates.add(sbd);}else {if (debugEnabled) {logger.debug("Ignored because not a concrete top-level class: " + resource);}}}else {if (traceEnabled) {logger.trace("Ignored because not matching any filter: " + resource);}}}catch (FileNotFoundException ex) {if (traceEnabled) {logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());}}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);}}}catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}return candidates;}
ComponentScanBeanDefinitionParser.registerComponents( XmlReaderContext readerContext, Set beanDefinitions, Element element)
protected void registerComponents(XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {Object source = readerContext.extractSource(element);// 构建CompositeComponentDefinitionCompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);// 将所有BeanDefinition添加到compositeDef的nestedComponents属性中for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));}// Register annotation config processors, if necessary.// 处理“annotation-config”:假定annotation-config是存在,这意味着配置了“context:component-scan”,则不需要再配置“context:annotation-config”boolean annotationConfig = true;if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {// 获取component-scan标签的annotation-config属性值(默认为true)annotationConfig = Boolean.parseBoolean(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));}if (annotationConfig) {// 获取所有处理注解类的BeanPostProcessors(BeanPostProcessor本身也是bean)Set<BeanDefinitionHolder> processorDefinitions =AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);// 将所有BeanPostProcessors的BeanDefinition添加到compositeDef的nestedComponents属性中for (BeanDefinitionHolder processorDefinition : processorDefinitions) {compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));}}// 触发组件注册事件readerContext.fireComponentRegistered(compositeDef);}
相关文章:
一起学SF框架系列5.8-spring-Beans-Bean注解解析3-解析配置component-scan
本文主要讲述Spring是如何解析“context:component-scan”元素,扫描加载目录下的BeanDefinition。 解析内容 1、解析的元素如下: <!-- 注解模式:配置bean扫描路径(注:自动包含子路径) --><conte…...
【LeetCode热题100】打卡第42天:滑动窗口最大值搜索二维矩阵II
文章目录 【LeetCode热题100】打卡第42天:滑动窗口最大值&搜索二维矩阵II⛅前言 滑动窗口最大值🔒题目🔑题解 搜索二维矩阵II🔒题目🔑题解 【LeetCode热题100】打卡第42天:滑动窗口最大值&搜索二维…...
[uni-app] 微信小程序 - 组件找不到/导入报错 (分包问题导致)
文章目录 问题表现问题原因 问题表现 切换了个路径下的组件, 导入失败, 尝试了清缓存\重启\删项目等一些列操作均无效 上面两个路径中, 都存在一模一样的videItem.vue Main路径是可以导入的 Main路径是无法导入的 问题原因 后来发现, 是 分包的问题导致. 我们先来假设一个场…...
从零构建医疗领域知识图谱的KBQA问答系统:其中7类实体,约3.7万实体,21万实体关系。
项目设计集合(人工智能方向):助力新人快速实战掌握技能、自主完成项目设计升级,提升自身的硬实力(不仅限NLP、知识图谱、计算机视觉等领域):汇总有意义的项目设计集合,助力新人快速实…...
编程小白的自学笔记十二(python爬虫入门四Selenium的使用实例二)
系列文章目录 编程小白的自学笔记十一(python爬虫入门三Selenium的使用实例详解) 编程小白的自学笔记十(python爬虫入门二实例代码详解) 编程小白的自学笔记九(python爬虫入门代码详解) 目录 系列文章…...
技术笔记2023076 rBoot学习7
技术笔记2023076 rBoot学习7 继续之前的学习。 代码分析:函数find_image() // prevent this function being placed inline with main // to keep mains stack size as small as possible // dont mark as static or itll be optimised out when // using the ass…...
收藏这6个抠图工具,一键抠图不用愁!
在图片编辑工作中,抠图是设计师常用的操作。随着设计工具的不断增加,抠图操作摆脱了过去繁琐的操作步骤,几乎可以一键完成。今天本文将为大家介绍6个好用的抠图工具,一起来看看吧! 1、皮卡智能抠图 皮卡智能抠图是一…...
四,Eureka 第四章
2.1.3 增加依赖 <!--添加依赖--><dependencies><!--Eureka Server--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency>&l…...
k8s常见的资源对象使用
目录 一、kubernetes内置资源对象 1.1、kubernetes内置资源对象介绍 1.2、kubernetes资源对象操作命令 二、job与cronjob计划任务 2.1、job计划任务 2.2、cronjob计划任务 三、RC/RS副本控制器 3.1、RC副本控制器 3.2、RS副本控制器 3.3、RS更新pod 四、Deployment副…...
JavaScript 简单实现观察者模式和发布订阅模式
JavaScript 简单实现观察者模式和发布订阅模式 1. 观察者模式1.1 如何理解1.2 代码实现 2. 发布订阅模式2.1 如何理解2.2 代码实现 1. 观察者模式 1.1 如何理解 概念:观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时ÿ…...
高通WLAN框架学习(37)-- TDLS(Tunneled Direct Link Setup)通道直接链路建立
一 TDLS概述 隧道直连设置(TDLS)基于IEEE 802.11z-2010IEEE标准802.11z标准(无线局域网介质访问控制(MAC)和物理层(PHY)规范。 TDLS允许与同一AP关联的设备之间建立直接链路。Wi-Fi Direct允许设备之间直接连接,而不需要AP。Wi-Fi联盟认证可用于IEEE 802.11a和802.11g设备的T…...
高算力AI模组前沿应用:基于ARM架构的SoC阵列式服务器
本期我们带来高算力AI模组前沿应用,基于ARM架构的SoC阵列式服务器相关内容。澎湃算力、创新架构、异构计算,有望成为未来信息化社会的智能算力底座。 ▌性能优势AI驱动,ARM架构服务器加速渗透 一直以来,基于ARM架构的各类处理器…...
老年公寓人员定位管理系统:提升安全与关怀的智能解决方案
老年公寓作为提供安全居住环境和关怀服务的重要场所,面临着人员管理和安全控制的挑战。为了解决这些问题,老年公寓人员定位管理系统应运而生。基于为提供全面的安全管理和个性化关怀服务,华安联大便通过老年公寓人员定位管理系统的技术原理、…...
每日一题之两个字符串的删除操作
题目链接 给定两个单词 word1 和 word2 ,返回使得 word1 和 word2 **相同所需的最小步数。 每步 可以删除任意一个字符串中的一个字符。 示例 1: 输入: word1 "sea", word2 "eat" 输出: 2 解释: 第一步将 "sea" 变…...
nacos安装与基础配置
源码 https://github.com/alibaba/nacos https://gitee.com/mirrors/Nacos 编译 git clone https://github.com/alibaba/nacos.git cd nacos/ mvn -Prelease-nacos -Dmaven.test.skiptrue clean install -U ls -al distribution/target/// change the $version to your ac…...
GitHub Copilot:让开发编程变得像说话一样简单
引用: 人类天生就梦想、创造、创新。但今天,我们花太多时间被繁重的工作所消耗,花在消耗我们时间、创造力和精力的任务上。为了重新连接我们工作的灵魂,我们不仅需要一种更好的方式来做同样的事情,更需要一种全新的工…...
并发编程中锁的优化
在 Java 并发编程中,锁是一种常用的同步机制,用于控制对共享资源的访问。使用锁可以确保多个线程之间的互斥访问,避免数据竞争和并发问题。 然而,锁的使用可能会带来一定的性能开销,特别是在高并发场景下。 为了优化…...
笔试题:统计字符串中某字符串在其出现的字符个数
笔试题:统计字符串中某一子串的字符个数:例如字符串aabbcd,有aabb:4,ab:2 哈哈,这道题是小编面试音视频龙头企业的笔试题,以下是我写的代码:如果有错误,希望可以指正!!! 解题思路:利用双指针i和…...
Java NIO Files类读取文件流方式详解
Java NIO Files类读取文件流方式详解 Files类原理概述 java.nio.file.Files是Java标准库提供的一个工具类,用于操作文件和目录。它提供了一系列静态方法,可以用于创建、复制、删除、移动、重命名、读取、写入文件和目录等常见的文件系统操作。同时&…...
Mybatis快速入门,Mybatis的核心配置文件
Mybatis快速入门 一、Mybatis简介1.1Mybatis简化JDBC 二、Mybatis快速入门2.1创建user表,添加数据2.2创建模块,导入坐标2.3编写Mybatis核心配置文件 --> 替换连接信息,解决硬编码问题2.4编写SQL映射文件 --> 统一管理sql语句࿰…...
苏州创新药20年,站上全球产业洗牌暴风眼
一个城市的创新药产业集群如何从无到有,又如何在全球化临界点寻找自己的位置。文|徐鑫编|任晓渔过去一年多,苏州是全球创新药产业版图中一个绕不过去的城市。大额海外授权交易频繁传出,在中国高端制造走出去的背景下&a…...
FM3773 低功耗离线式恒流/恒压 PSR 控制器
概述 FM3773 是一种高性能的交流/直流用于电池充电器和适配器的电源控制器,内置 850V 功率三极管。该设备采用脉冲频率调制(PFM)的方法来建立非连续导通模式(DCM)反激式电源。 FM3773 提供精确的恒定电压,恒…...
CUDA并行计算与FSR框架优化实践
1. CUDA并行计算与FSR框架概述在GPU加速计算领域,CUDA(Compute Unified Device Architecture)作为NVIDIA推出的并行计算平台和编程模型,已经成为高性能计算的事实标准。其核心设计理念是将计算任务分解为网格(Grid&…...
Atomic Layout核心概念解析:Composition组件如何实现布局与间距分离的终极指南
Atomic Layout核心概念解析:Composition组件如何实现布局与间距分离的终极指南 【免费下载链接】atomic-layout Build declarative, responsive layouts in React using CSS Grid. 项目地址: https://gitcode.com/gh_mirrors/at/atomic-layout Atomic Layout…...
DeepSeek代码风格检查避坑指南(内部审计报告首次披露:37个被忽略的合规红线)
更多请点击: https://intelliparadigm.com 第一章:DeepSeek代码风格检查的合规性本质与审计背景 DeepSeek代码风格检查并非单纯的技术偏好约束,而是嵌入研发治理链条中的合规性控制节点。其本质是将编程实践与组织级安全策略、行业监管要求&…...
2026这6款神级降AIGC平台大公开,一键让AIGC率直逼绝对安全线!
步入 2026 年,学术圈的风向早已不是从前的模样。曾经大家还在为查重率发愁,如今却陷入了更棘手的困境——如何在不破坏论文专业性的前提下,彻底消除 AI 痕迹?随着 AIGC 检测技术不断进化,高校对论文的审核标准也愈发严…...
Xia Sql插件:可调试的SQL注入决策引擎
1. 这不是又一个“自动扫SQL”的插件,而是把渗透工程师的判断逻辑塞进了Burp里你有没有过这种经历:在Burp Proxy里看着一堆GET参数、POST JSON、Cookie字段,心里清楚“这里大概率能注入”,但手动拼payload试了七八轮,还…...
告别KITTI!用TartanAir数据集在Unreal Engine+AirSim里复现那些让VSLAM算法“翻车”的雨天和黑夜
超越KITTI:用TartanAir数据集在虚拟极端环境中锤炼VSLAM算法当视觉SLAM算法在KITTI数据集上取得95%的准确率时,开发者们常常会松一口气——直到这些算法被部署到真实世界的雨夜街道上。突然之间,那些在阳光明媚的德国道路上表现优异的特征点检…...
机器学习赋能矩方法:破解稀薄气体强非平衡流动模拟难题
1. 项目概述:当矩方法遇见机器学习在计算流体力学领域,模拟稀薄气体动力学和强非平衡流动,一直是个让工程师和科学家们头疼的“硬骨头”。想象一下,你正在设计一架高超音速飞行器,当它以数倍音速在大气层边缘飞行时&am…...
Lovable电商网站搭建,为什么92%的初创团队在第3周就遭遇性能雪崩?
更多请点击: https://codechina.net 第一章:Lovable电商网站搭建 Lovable 是一个面向中小商户的轻量级电商解决方案,采用现代 Web 技术栈构建,强调可扩展性、用户体验与快速部署。其核心基于 Vue 3(Composition API&a…...
