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

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

探讨 Spring Bean 的作用域机制,对比单例、原型及 Web 相关作用域的区别,重点解析 Request 作用域下的代理注入原理。深入 Bean 生命周期五个阶段,结合源码揭示初始化回调与后置处理的执行顺序差异及 BeanPostProcessor 递归初始化导致的日志困惑。最后剖析 Spring Boot 自动装配原理,分析@EnableAutoConfiguration 如何通过 ImportSelector 加载配置类,涵盖 spring.factories 与 .imports 文件的演进关系。

黑客发布于 2026/3/27更新于 2026/9/1671 浏览
Spring Bean 作用域、生命周期与自动装配源码解析

Bean 的作用域

Spring Bean 的作用域定义了 Bean 在哪些上下文中可用。默认情况下是单例(Singleton),但在 Web 环境或特定场景下,原型(Prototype)、请求(Request)等作用域更为关键。

/**
 * 实体类
 */
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();
    }

    // 请求
    @Bean
    @RequestScope
    public Dog requestDog() {
        return new Dog();
    }

    // 会话
    @Bean
    @SessionScope
    public Dog sessionDog() {
        return new Dog();
    }

    // 应用
    @Bean
    @ApplicationScope
    public Dog applicationDog() {
        return new Dog();
    }
}

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

/**
 * 视图层
 */
@RequestMapping("/test")
@RestController
public class TestController {
    private final Dog single;
    private final Dog prototype;
    private final Dog request;
    private final Dog session;
    private final Dog application;
    private final ApplicationContext context;

    public TestController(@Qualifier("singleDog") Dog single,
                          @Qualifier("prototypeDog") Dog prototype,
                          @Qualifier("requestDog") Dog request,
                          @Qualifier("sessionDog") Dog session,
                          @Qualifier("applicationDog") Dog application,
                          ApplicationContext context) {
        this.single = single;
        this.prototype = prototype;
        this.request = request;
        this.session = session;
        this.application = application;
        this.context = context;
    }

    @RequestMapping("/single")
    public String single() {
        Dog singleDog = (Dog) context.getBean("singleDog");
        return "dog: " + this.single.toString() + "<br>" + "contextDog: " + singleDog;
    }

    @RequestMapping("/prototype")
    public String prototype() {
        Dog prototypeDog = (Dog) context.getBean("prototypeDog");
        return "dog: " + this.prototype.toString() + "<br>" + "contextDog: " + prototypeDog;
    }

    @RequestMapping("/request")
    public String request() {
        Dog requestDog = (Dog) context.getBean("requestDog");
        return "dog: " + this.request.toString() + "<br>" + "contextDog: " + requestDog;
    }

    @RequestMapping("/session")
    public String session() {
        Dog sessionDog = (Dog) context.getBean("sessionDog");
        return "dog: " + this.session.toString() + "<br>" + "contextDog: " + sessionDog;
    }

    @RequestMapping("/application")
    public String application() {
        Dog applicationDog = (Dog) context.getBean("applicationDog");
        return "dog: " + this.application.toString() + "<br>" + "contextDog: " + applicationDog;
    }
}

直接通过 context.getBean() 获取会触发新实例创建。这里有个常见的坑:当使用 @Autowired 或 @Resource 注入 Prototype 作用域的 Bean 时,注入操作仅在容器初始化阶段执行一次,后续访问的是最初注入的实例,不会自动重新注入。

对于 Request 和 Session 作用域,Spring 实际上注入的是一个代理对象而非真实实例。代理对象在应用启动时就被注入到依赖它的单例 Bean 中,但真实实例的创建被延迟到 HTTP 请求发生时。代理对象内部持有对当前 HTTP 请求上下文的引用,调用方法时会从上下文中查找或创建新的真实实例。这种机制确保了每个请求线程都能获得独立的实例。

  • Singleton(单例):默认作用域,每个 Spring 容器中仅存在一个 Bean 实例。
  • Prototype(原型):每次请求 Bean 时都会创建一个新的实例。
  • Request(请求):每个 HTTP 请求创建一个新的 Bean 实例,仅在 Web 应用中有效。
  • Session(会话):每个用户会话创建一个 Bean 实例,仅在 Web 应用中有效。
  • Application(应用):整个 Web 应用共享一个 Bean 实例。

Bean 的生命周期

Bean 的生命周期指的是对象从创建到销毁的整个过程,主要分为五个部分:

  1. 实例化:容器通过反射调用 Bean 的构造器创建对象实例。
  2. 属性赋值:容器注入依赖的属性值(例如 @Autowired)。
  3. 初始化:
    • 通知方法调用:通过特定接口(如 BeanNameAware)注入框架相关依赖。
    • 前置处理:进行准备工作,例如参数校验、资源加载。
    • 初始化回调:触发自定义逻辑(如实现 InitializingBean 接口的 afterPropertiesSet 方法)。
    • 后置处理:流程结束后执行清理或结果处理(如 AOP 中的 @After 通知)。
  4. 使用 Bean:Bean 进入就绪状态,可被应用程序调用。
  5. 销毁 Bean:容器关闭时触发。

示例代码

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

/**
 * 配置类
 */
@Configuration
public class CatConfig {
    @Bean
    public Cat cat() {
        return new Cat();
    }
}

/**
 * 实现类
 */
@Component
@Slf4j
public class BeanLifeComponent implements BeanNameAware, BeanPostProcessor, InitializingBean {
    private Cat cat;

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

    // 2. 属性赋值:执行 setter 方法
    @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");
    }
}

/**
 * 测试类
 */
@SpringBootTest
class SpringPrincipleApplicationTests {
    private final ApplicationContext context;

    @Autowired
    public SpringPrincipleApplicationTests(ApplicationContext context) {
        this.context = context;
    }

    @Test
    public void test() {
        BeanLifeComponent beanLifeComponent = context.getBean(BeanLifeComponent.class);
        beanLifeComponent.use();
    }
}

运行日志显示顺序往往是:初始化回调 → 前置处理 → 后置处理。这看起来与理论流程相矛盾,其实是因为 BeanPostProcessor 本身也是 Bean。

当 Spring 初始化一个 BeanPostProcessor 实现类时,这个过程是递归的。Spring 需要先让这个 BeanPostProcessor 对象本身完成初始化(调用 invokeInitMethods),然后才能将它加入到列表中供后续其他 Bean 使用。但对于这个 BeanPostProcessor 自己来说,它自己的 afterPropertiesSet 会在初始化回调阶段执行,而它自己的 postProcessBeforeInitialization 和 postProcessAfterInitialization 方法不会在它自己的创建过程中被调用。因此,日志显示的其实是该 Bean 作为普通 Bean 被其他已初始化的 Processor 处理的过程。

根据源码分析,正确的生命周期流程应该是:前置处理 → 初始化回调 → 后置处理。

源码解析

核心类 AbstractAutowireCapableBeanFactory 负责 Bean 的创建、依赖注入及初始化管理。

  1. createBean 方法:调用了 doCreateBean 方法。
  2. doCreateBean 方法:依次调用了 createBeanInstance(实例化)、populateBean(属性赋值)和 initializeBean(初始化)。
  3. initializeBean 方法:依次调用了 invokeAwareMethods(通知方法)、applyBeanPostProcessorsBeforeInitialization(前置处理)、invokeInitMethods(初始化回调)和 applyBeanPostProcessorsAfterInitialization(后置处理)。

SpringBoot 自动装配

自动装配的作用是注册 Bean 到 Spring 容器,不需要手动配置,通过'约定大于配置'的方式减少复杂性。简单来说,就是将依赖 Jar 包中的配置类以及 Bean 加载到 IoC 容器的过程。

加载 Bean 的几种方式

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

1. @ComponentScan

告诉 Spring 容器去哪里扫描标记了 @Component、@Service 等注解的类。

@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", "com.example.springprincicle.component.DemoConfig"};
    }
}
3. 自定义注解

为了降低耦合,通常由依赖开发者提供一个注解,内部封装 @Import。

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

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

@SpringBootApplication 源码解析

这是一个组合注解,包含三个核心功能:

  1. @ComponentScan:默认扫描启动类所在目录及其子目录。排除不需要扫描的配置类,防止重复注册。
  2. @SpringBootConfiguration:标记该类为 Spring 配置类,并提供索引支持加速启动。
  3. @EnableAutoConfiguration:这是自动装配的核心。

AutoConfigurationImportSelector 实现了 DeferredImportSelector 接口,负责动态加载自动配置类。在 Spring Boot 3.x 中,遵循'约定优于配置',第三方依赖库需要将其自动配置类定义在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中。

getCandidateConfigurations 方法会扫描类路径下的 .imports 文件,读取所有找到的自动配置类名。同时,为了保持向后兼容,Spring Framework 仍然保留了 spring.factories 的支持,通过 SpringFactoriesLoader 加载相关的监听器事件。

这样,Spring Boot 启动时就能自动发现并加载这些配置,无需开发者手动干预。

目录

  1. Bean 的作用域
  2. Bean 的生命周期
  3. 示例代码
  4. 源码解析
  5. SpringBoot 自动装配
  6. 加载 Bean 的几种方式
  7. 1. @ComponentScan
  8. 2. @Import
  9. 3. 自定义注解
  10. @SpringBootApplication 源码解析

更多推荐文章

查看全部
  • LangChain 入门指南:大模型应用开发基础
  • 架构演进实战:从单机到分布式,Redis 的核心价值
  • OpenSpec 实战:用规范驱动开发解决 AI 编程协作难题
  • Java static 避坑:静态与非静态访问规则全解析
  • MySQL 数据库存储引擎的数据结构
  • Java AI 开发实战:从代码生成到架构优化指南
  • GitHub 日榜:AI 智能体与前沿工具精选
  • AI + 鸿蒙游戏,会成为下一个爆点吗?
  • LTX-2.3:开源 AI 视频生成新标杆,支持音视频同步生成
  • GitHub Copilot 提示词工程实战:从基础使用到高效协作
  • Home Assistant 个性化 UI 设计指南
  • HarmonyOS 应用开发实战 Day06:常见组件
  • C++11 核心特性详解:Lambda、可变参数与函数包装器
  • ToDesk ToClaw:基于 OpenClaw 的零门槛 AI 桌面自动化方案
  • ArrowDL BT 下载工具 v4.2.1 中文版安装教程
  • 论文降重与 AIGC 检测规避的合规优化方案
  • Unity VR 眼镜端高分辨率全景视频播放性能优化
  • LLaMA-Factory 全流程模型训练指南
  • Qwen-Multiple-Angles 多视角图像生成插件详解
  • C语言标准库与工具链:string.h、stdio.h、stdlib.h及CMake构建

相关免费在线工具

  • 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