跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Spring Bean 作用域、生命周期与自动装配源码解析

深入解析 Spring Bean 的五大作用域机制,重点阐述 Request 作用域下的代理注入原理。详细拆解 Bean 生命周期的五个阶段,解释 BeanPostProcessor 递归初始化导致的日志顺序困惑。同时剖析 Spring Boot 自动装配原理,涵盖@ComponentScan、@Import 及 AutoConfigurationImportSelector 的工作流程,对比 spring.factories 与 .imports 文件的加载差异,帮助开发者理解依赖注入与配置管理的底层逻辑。

莫名其妙发布于 2026/3/29更新于 2026/6/321 浏览
Spring Bean 作用域、生命周期与自动装配源码解析

Spring Bean 作用域

Spring Bean 的作用域决定了实例在容器中的存在范围。理解这一点对于管理内存和线程安全至关重要。

/**
 * 实体类
 */
public class Dog {}

/**
 * 配置类
 */
@Configuration
public class DogConfig {
    // 单例:默认,整个容器仅一个实例
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
    public Dog singleDog() {
        return new Dog();
    }

    // 原型:每次请求都创建新实例
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Dog prototypeDog() {
        return new Dog();
    }

    // 请求:每个 HTTP 请求一个新实例
    @Bean
    @RequestScope
    public Dog requestDog() {
        return new Dog();
    }

    // 会话:每个用户会话一个新实例
    @Bean
    @SessionScope
    public Dog sessionDog() {
        return new Dog();
    }

    // 应用:整个 Web 应用共享一个实例
    @Bean
    @ApplicationScope
    public Dog applicationDog() {
        return new Dog();
    }
}

启动类如下:

@SpringBootApplication
public class SpringPrincipleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringPrincipleApplication.class, args);
    }
}

在控制器中注入这些 Bean 时,行为会有所不同:

  • 直接通过 context.getBean():会触发新实例的创建(针对非单例)。
  • @Autowired/@Resource 注入:
    • 对于 prototype 作用域的 Bean,注入操作仅在容器初始化阶段执行一次。后续通过字段访问的是最初注入的实例,不会自动更新。
    • 对于 request 作用域的 Bean,Spring 实际上注入的是一个代理对象。这个代理对象在启动时被注入到单例 Bean 中,但真实实例的创建被延迟到了 HTTP 请求发生时。

核心机制说明:

  1. 代理注入:当使用 @Autowired 注入 Request 作用域的 Bean 时,Spring 注入的是代理对象而非真实实例。代理对象持有当前 HTTP 请求上下文的引用。
  2. 方法调用:当调用代理对象的方法时,它会从当前请求的上下文中查找或创建新的真实实例。
  3. 实例操作:虽然依赖注入发生在容器初始化阶段,但通过代理模式将实例获取延迟到实际方法调用时,确保每个请求线程都能获得独立的实例。

Bean 作用域示意图

Bean 的生命周期

Bean 的生命周期涵盖了从创建到销毁的全过程,主要分为五个阶段:

  1. 实例化:容器通过反射调用构造器创建对象。
  2. 属性赋值:容器注入依赖的属性值(如 @Autowired)。
  3. 初始化:
    • 通知方法调用:实现 BeanNameAware 等接口注入框架信息。
    • 前置处理:BeanPostProcessor.postProcessBeforeInitialization,用于参数校验或资源加载。
    • 初始化回调:实现 InitializingBean 接口的 afterPropertiesSet 方法。
    • 后置处理:BeanPostProcessor.postProcessAfterInitialization,常用于 AOP 代理生成。
  4. 使用 Bean:进入就绪状态,可被应用程序调用。
  5. 销毁 Bean:容器关闭时触发 @PreDestroy 等方法。

示例代码

@Component
@Slf4j
public class BeanLifeComponent implements BeanNameAware, BeanPostProcessor, InitializingBean {
    private Cat cat;

    // 1. 实例化
    public BeanLifeComponent() {
        log.info("1.实例化:执行构造方法");
    }

    // 2. 属性赋值
    @Autowired
    public void setCat(Cat cat) {
        log.info("2.属性赋值:执行 setter 方法");
        this.cat = cat;
    }

    // 3.1 通知方法调用
    @Override
    public void setBeanName(String name) {
        log.info("3.1 通知方法调用,bean name is {}", name);
    }

    // 3.2 前置处理
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        log.info("3.3 前置处理,bean:{},beanName:{}", bean, beanName);
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    // 3.3 初始化回调
    @Override
    public void afterPropertiesSet() {
        log.info("3.2 初始化回调");
    }

    // 3.4 后置处理
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("3.4 后置处理,bean:{},beanName:{}", bean, beanName);
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }

    // 4. 使用 Bean
    public void use() {
        log.info("4.使用 Bean");
    }

    // 5. 销毁 Bean
    @PreDestroy
    public void preDestroy() {
        log.info("5.销毁 Bean");
    }
}

运行测试后,日志顺序可能显示为:初始化回调 → 前置处理 → 后置处理。这看起来与理论流程矛盾,其实是因为 BeanPostProcessor 本身也是 Bean。当 Spring 初始化一个实现了 BeanPostProcessor 的 Bean 时,这个过程是递归的。该 Bean 自身的 afterPropertiesSet 会在其初始化阶段执行,但它自己的 postProcessBeforeInitialization 方法不会在它自己的创建过程中被调用,而是由其他已初始化的处理器来调用它。因此,日志显示的其实是该 Bean 作为普通 Bean 被其他处理器处理的过程。

生命周期流程图

SpringBoot 自动装配

自动装配的核心在于'约定大于配置',Spring Boot 会自动扫描并注册依赖包中的配置类和 Bean,无需手动编写大量 XML 或注解。

加载 Bean 的原理

在 pom.xml 引入第三方依赖后,Spring Boot 启动时会识别这些依赖并加载其配置。如果自定义的配置类不在默认扫描路径下,可以通过以下方式解决:

1. @ComponentScan

指定扫描路径,告诉容器去哪里寻找组件。

@SpringBootApplication
@ComponentScan("com.example.springprincicle.component")
public class SpringPrincipleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringPrincipleApplication.class, args);
    }
}
2. @Import

显式导入配置类或组件类。

@SpringBootApplication
@Import(TestConfig.class)
public class SpringPrincipleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringPrincipleApplication.class, args);
    }
}

或者通过 ImportSelector 接口动态选择要导入的类:

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.example.springprincicle.component.TestConfig"};
    }
}
3. 自定义注解封装

为了更友好地暴露功能,通常会将 @Import 封装在自定义注解中。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyImportSelector.class)
public @interface EnableTestConfig {}

@SpringBootApplication 源码解析

这是一个组合注解,包含三个核心部分:

  1. @ComponentScan:默认扫描启动类所在包及其子包。如果没有指定路径,则以此为根目录。
  2. @SpringBootConfiguration:标记该类为配置类,支持索引加速启动。
  3. @EnableAutoConfiguration:开启自动配置。
自动配置加载流程

@EnableAutoConfiguration 内部使用了 AutoConfigurationImportSelector 类。该类实现了 DeferredImportSelector 接口,负责动态加载自动配置类。

在 Spring Boot 3.x 版本中,加载逻辑主要遵循以下路径:

  • 扫描类路径下的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件。
  • 读取文件中定义的自动配置类名列表。
  • 排除被禁用的配置类(通过 spring.autoconfigure.exclude 配置)。

旧版本的 spring.factories 机制在 Spring Boot 3.x 中依然保留以兼容旧项目,但推荐使用 .imports 文件。这种设计体现了 Spring Boot 向后兼容的理念。

// AutoConfigurationImportSelector 关键逻辑示意
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, getBeanClassLoader());
    List<String> configurations = importCandidates.getCandidates();
    // ... 过滤和去重 ...
    return configurations;
}

通过这种方式,第三方库只需在资源文件中声明配置类,Spring Boot 启动时便能自动发现并加载,极大简化了集成过程。

目录

  1. Spring Bean 作用域
  2. Bean 的生命周期
  3. 示例代码
  4. SpringBoot 自动装配
  5. 加载 Bean 的原理
  6. 1. @ComponentScan
  7. 2. @Import
  8. 3. 自定义注解封装
  9. @SpringBootApplication 源码解析
  10. 自动配置加载流程
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • OpenClaw 自定义 Skill 开发实战:Excel 数据批量处理
  • 秋叶绘世 Stable Diffusion 整合包与 ComfyUI 使用指南
  • rest_rpc 框架:现代 C++ 高性能 RPC 库实战指南
  • 前端请求后端 404/405/500 状态码排查与解决
  • MySQL 数据类型深度解析:选型策略与避坑指南
  • 本地部署 LLaMA 模型:新版 llama.cpp 实战指南
  • 滑动窗口实战:长度最小的子数组与无重复字符的最长子串
  • 偏好对齐技术:通用模型蒸馏、领域模型蒸馏与自我提升
  • 基于 SpringBoot2+Vue3 的大学生科创项目在线管理系统
  • OpenClaw v2026.3.1 版本更新与核心功能解析
  • CentOS 系统安装 Docker 完整指南
  • Win11 本地部署 OpenClaw:集成 Telegram 机器人与网页搜索功能
  • OpenClaw 开源 AI 智能体项目精选与部署指南
  • Gitee 代码上传指南:Git 基础与实战操作
  • Java 基于百度天气 API 实现海外城市实时天气获取
  • 宇树机器人强化学习:PPO 算法 Python 实现与解析
  • Git 与 TortoiseGit 详细安装及使用指南
  • 本地 Qwen 与 ComfyUI 制作 AI 漫剧教程
  • OpenClaw 安装与飞书机器人配置实战指南
  • OpenClaw 公网穿透实战:让本地 AI 随时随地可用

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online