Spring Boot 的注解是如何生效的

在这里插入图片描述
在这里插入图片描述

在 Spring 中,@Configuration@ComponentScan@Bean@Import 等注解的扫描、解析和 BeanDefinition 注册是一个分层处理的过程。下面我们以 @Configuration为例,结合代码流程详细说明其从扫描到注册的完整逻辑。

1. 整体流程概览

以下是核心步骤的流程图:

1. 扫描候选配置类 → 2. 解析注解元数据 → 3. 注册 BeanDefinition

具体分为以下阶段:

  1. 扫描阶段:通过 BeanDefinitionRegistry 获取所有候选配置类。
  2. 解析阶段:使用 ConfigurationClassParser 解析注解(如 @ComponentScan@Bean@Import)。
  3. 注册阶段:通过 ConfigurationClassBeanDefinitionReader 将解析结果注册为 BeanDefinition

2. 详细步骤解析

2.1 扫描阶段:识别候选配置类

触发入口
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()
逻辑

  1. BeanDefinitionRegistry 获取所有已注册的 BeanDefinition 名称:
    String[] beanNames = registry.getBeanDefinitionNames();
    
  2. 遍历这些名称,检查对应的 BeanDefinition 是否是候选配置类:
    • 条件:类上有 @Configuration@Component@ComponentScan@Import@ImportResource,或类中有 @Bean 方法。
    • 判断逻辑
      if (isFullConfigurationCandidate(beanDef) || isLiteConfigurationCandidate(beanDef)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
      }
      
    • isFullConfigurationCandidate(beanDef):检查是否有 @Configuration 注解。
    • isLiteConfigurationCandidate(beanDef):检查是否有其他相关注解(如 @Component@Bean 方法)。

关键点

  • 扫描的输入是已注册的 BeanDefinition(可能来自 XML、Java Config 或自动扫描)。
  • 此时尚未解析注解内容,仅识别出需要进一步处理的候选类。

2.2 解析阶段:处理注解元数据

核心类ConfigurationClassParser
入口方法parse()
逻辑:递归解析每个候选配置类的注解。

(1) 解析 @ComponentScan
  • 作用:扫描指定包路径下的 @Component 类(如 @Service@Repository)。
  • 流程
    1. 获取 @ComponentScan 注解的 basePackagesbasePackageClasses
    2. 使用 ClassPathBeanDefinitionScanner 扫描类路径:
      scanner.scan(basePackages);
      
    3. 扫描到的类会被注册为新的 BeanDefinition(类型为 ScannedGenericBeanDefinition)。
  • 关键点
    • 扫描时使用 ASM 或反射读取类注解,避免提前加载类到 JVM。
    • 新注册的 BeanDefinition 可能也会被后续解析(如果它们也是配置类)。
(2) 解析 @Bean 方法
  • 作用:将配置类中的 @Bean 方法转换为 BeanDefinition
  • 流程
    1. 遍历配置类中的所有方法,筛选带 @Bean 注解的方法。
    2. 为每个 @Bean 方法生成一个 BeanDefinition
      • 类型ConfigurationClassBeanDefinition
      • 工厂方法:设置为 @Bean 方法(通过 factoryMethodNamefactoryBeanName 指定)。
      • 依赖:解析 @Bean 方法的参数(按类型或 @Qualifier 注入)。
  • 示例
    @Configuration
    public class AppConfig {@Beanpublic DataSource dataSource() {return new HikariDataSource();}
    }
    
    • 生成的 BeanDefinition 会记录:factoryBeanName=appConfig, factoryMethodName=dataSource
(3) 解析 @Import
  • 作用:动态导入其他配置类或 BeanDefinition
  • 三种处理方式
    1. 普通类:直接注册为 BeanDefinition
      @Import(OtherConfig.class)
      
    2. ImportSelector:通过编程方式选择要导入的类。
      @Import(MyImportSelector.class)
      
      • MyImportSelector 实现 selectImports() 方法,返回要导入的类名数组。
    3. ImportBeanDefinitionRegistrar:直接注册 BeanDefinition
      @Import(MyRegistrar.class)
      
      • MyRegistrar 实现 registerBeanDefinitions() 方法,手动操作 BeanDefinitionRegistry
(4) 处理父类与接口
  • 递归检查配置类的父类和接口,确保不遗漏任何 @Bean 方法或元注解。

2.3 注册阶段:加载 BeanDefinition

核心类ConfigurationClassBeanDefinitionReader
入口方法loadBeanDefinitions()
逻辑:将解析结果(ConfigurationClass 对象)转换为 BeanDefinition 并注册到容器。

(1) 注册 @Import 的类
  • 普通类:通过 registry.registerBeanDefinition() 直接注册。
  • ImportBeanDefinitionRegistrar:调用其 registerBeanDefinitions() 方法。
(2) 注册 @Bean 方法
  • 为每个 @Bean 方法生成 BeanDefinition 并注册:
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {loadBeanDefinitionsForBeanMethod(beanMethod);
    }
    
(3) 处理嵌套配置类
  • 如果配置类内部有 @Configuration 静态嵌套类,递归处理。

3. 关键设计点

(1) 延迟加载与递归处理

  • 延迟加载@ComponentScan 扫描到的类可能也是配置类,需要递归解析。
  • 循环依赖处理:Spring 通过提前暴露 BeanDefinition 解决配置类之间的循环引用。

(2) 元数据存储

  • ConfigurationClass 对象存储解析后的中间结果(如 @Bean 方法、@Import 类等)。
  • BeanDefinitionattribute 字段存储配置类的元信息(如 @Lazy@Primary)。

(3) 性能优化

  • ASM 字节码分析:在扫描阶段避免加载类到 JVM。
  • 缓存:解析结果缓存到 ConfigurationClass 中,避免重复处理。

4. 示例全流程

场景

@Configuration
@ComponentScan("com.example.service")
@Import(OtherConfig.class)
public class AppConfig {@Beanpublic DataSource dataSource() {return new HikariDataSource();}
}

步骤

  1. 扫描阶段
    • 发现 AppConfig 是候选配置类(有 @Configuration)。
  2. 解析阶段
    • 解析 @ComponentScan:扫描 com.example.service 包,注册 @Service 类。
    • 解析 @Import(OtherConfig.class):递归处理 OtherConfig
    • 解析 @Bean dataSource():生成工厂方法 BeanDefinition
  3. 注册阶段
    • 注册 OtherConfig 及其 @Bean 方法。
    • 注册 dataSourceBeanDefinition

5. 总结

Spring 对配置类注解的处理是一个分层递归的过程:

  1. 扫描:通过 BeanDefinitionRegistry 筛选候选类。
  2. 解析ConfigurationClassParser 解析注解并生成中间结果(ConfigurationClass)。
  3. 注册ConfigurationClassBeanDefinitionReader 将解析结果转换为 BeanDefinition

这种设计将注解元数据解析与 BeanDefinition 注册分离,确保了灵活性和扩展性(如支持动态 ImportSelector)。同时,递归处理和缓存机制解决了复杂依赖和性能问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/96349.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/96349.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Django REST Framework响应类Response详解

概述 Response 类是一个智能的 HTTP 响应类,能够根据客户端请求的内容类型(Content-Type)自动将数据渲染成合适的格式(JSON、XML、HTML等)。 基本用法 from rest_framework.response import Response# 最简单的用法 de…

# 小程序 Web 登录流程完整解析

登录流程完整小白解析(小程序 & Web) 在开发中,登录是每个系统最基础的功能。为了让小白也能理解,我们用通俗类比和流程讲解 小程序登录、Web 登录、Token 刷新、安全存储等整个过程。1️⃣ 小程序登录流程(小白理…

安装vcenter6.7 第二阶段安装很慢 或卡在50%

DNS、FQDN配置的问题采用VCSA安装vCenter时,第一步安装还算顺利,第二步就会安装失败,而且还特别慢,这是因为部署时需要DNS服务器,下面就是不采用DNS服务器的部署方案。第一步:正常安装,DNS就写本…

第十六届蓝桥杯软件赛 C 组省赛 C++ 题解

大家好,今天是 2025 年 9 月 11 日,我来给大家写一篇关于第十六届蓝桥杯软件赛 C 组省赛的C 题解,希望对大家有所帮助!!! 创作不易,别忘了一键三连 题目一:数位倍数 题目链接&…

项目帮助文档的实现

项目帮助文档的实现 代码如下&#xff1a; #ifndef __M_HELPER_H__ #define __M_HELPER_H__ #include <iostream> #include <fstream> #include <string> #include <vector> #include <sqlite3.h> #include <random> #include <sstream…

python逆向-逆向pyinstaller打包的exe程序反编译获取源代码

python逆向-逆向pyinstaller打包的exe程序反编译获取源代码 Pyinstaller pyinstaller 是一个用于将 Python 程序打包成独立可执行文件的工具&#xff0c;能够在没有 Python 解释器的情况下运行。 Python 脚本转换为 Windows、macOS 和 Linux 操作系统上的可执行文件。 把Python…

【SQL】-- sql having 和 where 的 区别

HAVING 和 WHERE 都是用来筛选数据的&#xff0c;但它们的应用场景有所不同。WHERE&#xff1a;用于筛选行数据&#xff0c;通常在 FROM 子句之后执行。它在分组操作 (GROUP BY) 之前应用&#xff0c;用来筛选出符合条件的记录。示例&#xff1a;SELECT name, age FROM employe…

MySQL,SQL Server,PostgreSQL三种数据库各自的优缺点,分别适用哪些场景

MySQL的优缺点及适用场景优点开源免费&#xff0c;社区版可商用&#xff0c;成本低。轻量级&#xff0c;安装配置简单&#xff0c;适合中小型项目。读写性能优异&#xff0c;尤其在OLTP&#xff08;在线事务处理&#xff09;场景下表现突出。支持主从复制、分片等扩展方案&…

Java 类加载机制双亲委派与自定义类加载器

我们来深入解析 Java 类加载机制。这是理解 Java 应用如何运行、如何实现插件化、以及解决一些依赖冲突问题的关键。一、核心概念&#xff1a;类加载过程一个类型&#xff08;包括类和接口&#xff09;从被加载到虚拟机内存开始&#xff0c;到卸载出内存为止&#xff0c;它的整…

Kaggle项目实践——Titanic: Machine Learning from Disaster

泰坦尼克号沉船事件是机器学习领域最经典的入门项目之一。Kaggle 上的 Titanic: Machine Learning from Disaster 竞赛&#xff0c;被无数人称为“机器学习的 Hello World”。 一、数据导入与清洗&#xff1a;让数据从 “杂乱” 变 “干净” 机器学习模型就像 “挑食的孩子”…

Qt C++ 复杂界面处理:巧用覆盖层突破复杂界面处理难题​之二

接上一篇&#xff0c;继续探索“覆盖层”的使用方法。 五、覆盖层进阶交互&#xff1a;从 “能绘制” 到 “好操作”​ 基础的绘制功能只能满足 “看得见” 的需求&#xff0c;实际开发中还需要 “能操作”—— 比如选中线条修改颜色、按 Delete 键删除线条、鼠标 hover 时高亮…

神经网络构成框架-理论学习

一、神经网络的基本组成与分类 1.1 神经网络的核心组成部分 神经网络是现代人工智能的基石&#xff0c;其设计灵感来源于生物神经系统的信息处理方式。作为工程师&#xff0c;了解神经网络的基本组成部分对于构建和优化模型至关重要。一个典型的神经网络主要由以下几个关键部分…

从0开始开发app(AI助手版)-架构及环境搭建

架构选择 前端React Native 后端Firebase 原因 环境准备 安装node 安装JDK 命令行工具&#xff1a;Node.js command prompt命令行查询Javav版本&#xff1a;javac -version使用nrm工具切换淘宝源&#xff1a;npx nrm use taobao安装yarn&#xff0c;替代npm下载工具&#x…

【性能测试】Jmeter工具快速上手-搭建压力测试脚本

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【Java】登神长阶 史诗般的Java成神之路 概念 性能测试是软件测试的重要分支&#xff0c;核心目标是通过模拟真实业务场景和负载压力&#xff0c;评估系统在不同条件下的性能表现&#xff0c;发现系统性…

oracle里的int类型

oracle里的int类型 在 ANSI SQL 标准 中&#xff0c;INTEGER 和 SMALLINT 是定义好的精确数值类型&#xff0c;但它们的 “长度”或“大小”并不是通过 (N) 括号来指定的&#xff08;如 INT(4)&#xff09;&#xff0c;这一点与 MySQL 等数据库的非标准扩展完全不同。 SMALLI…

前端学习之后端java小白(二)-sql约束/建表

一、约束SQL约束&#xff08;Constraints&#xff09;是用于限制表中数据的规则&#xff0c;确保数据的完整性和准确性。以下是主要的SQL约束类型&#xff1a; 主要约束类型&#xff1a; 1. NOT NULL 约束: 确保列不能包含空值 CREATE TABLE users (id INT NOT NULL,name VARC…

OpenCV:图像金字塔

文章目录一、什么是图像金字塔&#xff1f;二、图像金字塔的核心操作&#xff1a;采样与逆采样1. 向下采样&#xff08;pyrDown&#xff09;&#xff1a;从高分辨率到低分辨率步骤1&#xff1a;高斯滤波步骤2&#xff1a;删除偶数行与偶数列OpenCV实战代码效果特点2. 向上采样&…

LVS与Keepalived详解(一)负载均衡集群介绍

文章目录前言一、什么是LVS&#xff1f;二、四层&#xff08;L4&#xff09;负载均衡的最佳解决方案是什么&#xff1f;2.1解决方案分类与对比&#xff08;负载均衡的三种方式介绍&#xff09;2.1.1 硬件负载均衡 (Hardware Load Balancer)2.1.2 软件负载均衡 (Software Load B…

消息队列-kafka完结

基本概念和操作 基本概念 简单概念:::color4 Broker&#xff1a;如果将kafka比喻成数据仓库网络&#xff0c;那么Broker就是网络中的仓库节点&#xff0c;比如快递站&#xff0c;在该节点内可以独立运行&#xff0c;并且多个Broker可以连接起来&#xff0c;构建kafka集群Topic&…

Chromium 138 编译指南 Windows篇:环境变量配置与构建优化(三)

引言配置&#xff0c;往往决定成败。在软件开发的世界里&#xff0c;环境变量就像是一位无声的指挥家&#xff0c;默默地协调着各个组件的协同工作。对于Chromium 138这样一个拥有数千万行代码的超大型项目而言&#xff0c;正确的环境变量配置更是编译成功的关键所在。也许您曾…