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

深入源码:Spring Bean 作用域、生命周期与自动装配

深入解析 Spring Boot 中 Bean 的核心机制。首先介绍 Bean 的五大作用域(单例、原型、请求、会话、应用)及其在 Web 环境下的实例化行为差异,特别是代理注入对 Request 作用域的影响。其次详细阐述 Bean 的生命周期五个阶段,结合源码分析 AbstractAutowireCapableBeanFactory 中 createBean、populateBean 及 initializeBean 的执行顺序,解释 BeanPostProcessor 初始化时的递归逻辑导致的日志顺序问题。最后解析 Spring Boot 自动装配原理,包括@ComponentScan、@Import 及@EnableAutoConfiguration 如何通过 ImportSelector 加载配置类,实现约定优于配置的依赖管理。

数字游民发布于 2026/3/30更新于 2026/5/2832 浏览
深入源码:Spring Bean 作用域、生命周期与自动装配

SpringBoot 版本:3.5.8

1. Bean 的作用域

Spring Bean 的作用域定义了 Bean 的作用范围,即 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();
    }

    // 请求
    @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 时:注入操作仅在初始化阶段(Spring 容器创建和配置 Bean 的过程)执行一次,后续通过字段引用访问的是最初注入的实例,不会因后续请求自动重新注入新实例。
  • 代理注入:当使用 @Autowired 注入 Request 作用域的 Bean 时,Spring 实际上注入的是一个代理对象而非真实实例。代理对象在应用启动时就被注入到依赖它的单例 Bean 中,但真实实例的创建被延迟到 HTTP 请求发生时。
    • 方法调用:注入的代理对象内部持有对当前 HTTP 请求上下文的引用。当调用代理对象的方法时,代理会从当前请求的上下文中查找或创建新的真实实例。
    • 实例操作:虽然依赖注入发生在容器初始化阶段,但通过代理模式将实例的获取延迟到实际方法调用时,这种延迟查找机制确保每个请求线程都能获得独立的实例。

Application(应用):整个 Web 应用共享一个 Bean 实例。

Session(会话):每个用户会话创建一个 Bean 实例,仅在 Web 应用中有效。

Request(请求):每个 HTTP 请求创建一个新的 Bean 实例,仅在 Web 应用中有效。

Prototype(原型):每次请求 Bean 时都会创建一个新的实例。

Singleton(单例):默认作用域,每个 Spring 容器中仅存在一个 Bean 实例。

2. Bean 的生命周期

生命周期指的是一个对象从创建到销毁的整个生命过程。Bean 的生命周期分为以下 5 个部分:

  • 实例化:容器通过反射调用 Bean 的构造器创建对象实例。
  • 属性赋值:容器注入依赖的属性值(例如 @Autowired)。
  • 初始化:
    • 通知方法调用:通过特定接口(如 Spring 的 BeanNameAware)注入框架相关依赖或上下文信息。
    • 前置处理:进行准备工作,例如参数校验、资源加载或权限检查。确保主逻辑具备执行条件,避免运行时错误。
    • 初始化回调:在对象初始化阶段触发的自定义逻辑(如实现 InitializingBean 接口的 afterPropertiesSet 方法)。用于完成属性设置后的额外初始化操作。
    • 后置处理:在流程结束后执行清理或结果处理(如 AOP 中的 @After 通知)。
  • 使用 Bean:Bean 进入就绪状态,可被应用程序调用。
  • 销毁 Bean:容器关闭时触发销毁。

2.1 示例

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

日志显示的顺序是:invokeInitMethods(初始化回调) -> applyBeanPostProcessorsBeforeInitialization(前置处理) -> applyBeanPostProcessorsAfterInitialization(后置处理)。这看起来与上述介绍的生命周期流程相矛盾,具体解释放在源码解析后。

2.2 源码解析

AbstractAutowireCapableBeanFactory 类的作用:主要负责 Bean 的创建、依赖注入以及初始化等生命周期管理。

  • 搜索 AbstractAutowireCapableBeanFactory 类,并找到它的 createBean 方法。
  • doCreateBean 方法中依次调用了 createBeanInstance、populateBean 和 initializeBean 方法。

3.3 初始化:initializeBean 方法中依次调用了 invokeAwareMethods、applyBeanPostProcessorsBeforeInitialization、invokeInitMethods 和 applyBeanPostProcessorsAfterInitialization 方法。

2.3 日志与源码'冲突'的原因分析

根据源码来看,正确的生命周期流程是:前置处理 → 初始化回调 → 后置处理。与上述运行输出的日志相矛盾。

关键点:BeanPostProcessor 本身也是 Bean,当 Spring 初始化一个 BeanPostProcessor 实现类(BeanLifeComponent 类)时,这个过程是递归的。

执行流程分析:

  1. 创建非 BeanPostProcessor Bean(BeanLifeComponent 类):按照源码顺序正常执行:BeforeInitialization → InitMethods → AfterInitialization。
  2. 创建 BeanPostProcessor Bean 时:
    • Spring 需要先让这个 BeanPostProcessor 对象本身完成初始化(调用 invokeInitMethods)。
    • 然后才能将它加入到 BeanPostProcessor 列表中,供后续其他 Bean(BeanLifeComponent 类)使用。
    • 但对于这个 BeanPostProcessor Bean 自己来说:
      • 它自己的 afterPropertiesSet 方法会在 invokeInitMethods 中执行(初始化回调阶段)。
      • 但它自己的 postProcessBeforeInitialization(前置处理)/postProcessAfterInitialization(后置) 方法不会在它自己的创建过程中被调用!

**上述日志中的情况解释:**日志显示的是一个 BeanPostProcessor 实现类(BeanLifeComponent 类)的初始化过程:

  • 3.2 初始化回调 - 这个 BeanPostProcessor Bean 自己的 afterPropertiesSet()。
  • 3.3 前置处理 - BeanLifeComponent 类对这个 BeanPostProcessor Bean 的处理。
  • 3.4 后置处理 - BeanLifeComponent 类对这个 BeanPostProcessor Bean 的处理。

3. SpringBoot 自动装配

作用:自动注册 Bean 到 Spring 容器,不需要手动配置,通过约定大于配置的方式减少手动配置的复杂性。换言之,Spring Boot 的自动配置就是将依赖 Jar 包中的配置类以及Bean加载到Ioc 容器的过程。

3.1 SpringBoot 加载 Bean

在 pom.xml 文件中引入第三方依赖,实际上就是将第三方代码引入到 SpringBoot 项目中。SpringBoot 项目在启动时能识别这些依赖并自动将它们的配置类以及 Bean 加载到 Ioc 容器的过程。

Spring 通过 五大注解 + @Bean 可以将 Bean 加载到 Ioc 容器中,前提是这些注解类需要保证和 SpringBoot 启动类(@SpringBootApplication)在同一目录或者其子目录下。

编写代码:

/**
 * 配置类
 */
@Slf4j
@Component
public class TestConfig {
    public void demo() {
        log.info("demo");
    }
}

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

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

    @Test
    public void demo() {
        TestConfig testConfig = context.getBean(TestConfig.class);
        testConfig.demo();
    }
}

下面介绍几种解决方法:

3.1.1 @ComponentScan

作用:告诉 Spring 容器去哪里扫描那些被 @Component、@Service、@Repository、@Controller 等注解标记的类,并将它们自动注册为 Bean。

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

作用:用于将一个或多个配置类、组件类或其他类导入到当前的 Spring 应用上下文中。

用法一:导入单个类

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

用法二:ImportSelector 接口实现类

@Slf4j
@Component
public class DemoConfig {
    public void demo() {
        log.info("demo");
    }
}

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.example.springprincicle.component.TestConfig", "com.example.springprincicle.component.DemoConfig"};
    }
}

@SpringBootTest
public class SpringPrincipleApplicationTests {
    private final ApplicationContext context;

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

    @Test
    public void demo() {
        TestConfig testConfig = context.getBean(TestConfig.class);
        testConfig.demo();
        DemoConfig demoConfig = context.getBean(DemoConfig.class);
        demoConfig.demo();
    }
}
3.1.3 自定义注解

在使用 @Import 注解导入 Bean 时,需要程序员熟悉第三方依赖的所有配置类、组件类或其他类,这对于程序员开发程序十分不友好。所以,应该由依赖的开发者来做这件事。

比较常见的方案就是第三方依赖给我们提供一个注解,该注解内部封装 @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);
    }
}

3.2 @SpringBootApplication 源码解析

@SpringBootApplication 是一个组合注解,包含以下三个核心注解的功能。

3.2.1 @ComponentScan
  • 排除不需要扫描的配置类,防止自动配置类被重复扫描或注册。
  • 在 @SpringBootApplication 注解中,@ComponentScan 注解没有指定扫描路径,那么默认扫描路径为 @SpringBootApplication 标注的类的类路径。
3.2.2 @SpringBootConfiguration
  • 标记该类为 Spring 的配置类。
  • @Indexed:为 Spring 的组件扫描提供索引支持,加速应用启动时的类加载过程。
3.2.3 @EnableAutoConfiguration

AutoConfigurationImportSelector 类:它实现了 DeferredImportSelector 接口,负责在 Spring 应用启动时动态加载自动配置类。

// JDK 源码,AutoConfigurationImportSelector 类
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions):体现了 Spring Framework/Spring Boot 的向后兼容性设计。即使在 Spring Boot 3.x 中引入了新的 .imports 机制,Spring Framework 仍然保留了 spring.factories 的支持。

// JDK 源码,AutoConfigurationImportSelector 类
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
    List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
    if (!listeners.isEmpty()) {
        AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
        for (AutoConfigurationImportListener listener : listeners) {
            invokeAwareMethods(listener);
            listener.onAutoConfigurationImportEvent(event);
        }
    }
}

protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
    return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);
}

public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
    return forDefaultResourceLocation(classLoader).load(factoryType);
}

public static SpringFactoriesLoader forDefaultResourceLocation(@Nullable ClassLoader classLoader) {
    return forResourceLocation(FACTORIES_RESOURCE_LOCATION, classLoader);
}

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

遵循 Spring Boot 的'约定优于配置'理念,第三方依赖库需要将其自动配置类定义在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中,这样 Spring Boot 启动时就能自动发现并加载这些配置。

getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes):在 Spring Boot 3.5.8(本文所用的版本)中,该方法不仅加载 Spring Boot 默认的自动配置类,还会加载所有第三方库提供的自动配置类。

// JDK 源码,AutoConfigurationImportSelector 类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation, getBeanClassLoader());
    // 扫描类路径下的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件
    List<String> configurations = importCandidates.getCandidates();
    // 从 ImportCandidates 对象中获取所有找到的自动配置类名,这些类名是从 .imports 文件中读取的
    Assert.state(!CollectionUtils.isEmpty(configurations), "No auto configuration classes found in " +
        "META-INF/spring/" + this.autoConfigurationAnnotation.getName() + ".imports. If you " +
        "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

目录

  1. 1. Bean 的作用域
  2. 2. Bean 的生命周期
  3. 2.1 示例
  4. 2.2 源码解析
  5. 2.3 日志与源码“冲突”的原因分析
  6. 3. SpringBoot 自动装配
  7. 3.1 SpringBoot 加载 Bean
  8. 3.1.1 @ComponentScan
  9. 3.1.2 @Import
  10. 3.1.3 自定义注解
  11. 3.2 @SpringBootApplication 源码解析
  12. 3.2.1 @ComponentScan
  13. 3.2.2 @SpringBootConfiguration
  14. 3.2.3 @EnableAutoConfiguration
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • Linux 下 C/C++ 调试器 GDB/Cgdb 实战指南
  • AI 编程技能(Skill)详解与 Java 方法生成实战
  • 六款高实用性 C# 开源项目推荐
  • Python 基础语法详解:变量、类型、运算符与交互
  • 位运算核心原理与实战应用
  • 特殊儿童干预:基于 VoxCPM-1.5-TTS 的辅助沟通系统实践
  • Python 多任务编程:进程与线程实战指南
  • CCF-CSP 第 38 次认证:机器人路径搜索题解
  • whisper.cpp 性能优化与编译配置指南
  • C++ 位图与布隆过滤器:原理与实现
  • Python 入门:30 天零基础学习规划(每日 1 小时)
  • MySQL 动态分区管理:自动化与优化实践
  • Mac 系统下使用 OBS 与虚拟机实现抖音直播
  • 机器人脑部药物递送三大技术路径的可转化性分析
  • NewStarCTF2025 Week1 Web 解题复盘
  • 一切皆是映射:深入理解 DQN 的稳定性与收敛性
  • 大模型幻觉问题深度治理:技术体系、工程实践与未来演进
  • 算法:滑动窗口技巧
  • 基于 GitHub Pages 搭建 CosyVoice3 在线演示站点
  • 认知刷新,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