轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java

  • Spring

    • 基础

    • 框架

      • Spring容器初始化过程和Bean生命周期探究
        • 一、前言
        • 二、容器基础
          • 1、- 最基础的容器
          • 2、- 功能齐全的容器
          • 3、Aware接口 - Bean获取容器资源的后门
        • 三、容器启动流程
          • 1、refresh()方法 - 容器启动的十二个步骤
        • 四、Bean的一生
          • 1、Bean生命周期的完整流程
          • 2、生命周期扩展点
        • 五、- 容器启动时的"总工程师"
          • 1、Spring内置的BeanFactoryPostProcessor
          • 2、实战案例:MyBatis如何注册Mapper
          • 3、- 特殊的Bean工厂
        • 六、- Bean创建过程的“参谋”
          • 1、Spring内置的BeanPostProcessor
          • 2、自定义BeanPostProcessor
        • 七、初始化和销毁方法
          • 1、使用注解方式(推荐)
          • 2、使用接口方式
        • 八、Lifecycle接口 - 控制Bean的启停
          • 1、实现示例
          • 2、Lifecycle扩展接口
          • 2.1、SmartLifecycle
          • a、示例代码
          • 2.2、Phased接口
          • 3、Lifecycle与其他生命周期方式的区别
        • 九、总结
      • Spring容器:从依赖管理到注解配置全解析
      • Spring事件探秘
      • Spring AOP的应用
      • Spring 事务管理
      • Spring中的资源访问:Resource接口
      • Spring中的验证、数据绑定和类型转换
      • Spring表达式语言(SpEL)
      • Spring中的属性占位符
      • Spring数据缓冲区与编解码器详解
      • Spring对于原生镜像和AOT的支持
      • Spring中的集成测试与单元测试
      • Spring与多种技术的集成
      • Spring框架版本新特性
    • Spring Boot

    • 集成

  • 其他语言

  • 工具

  • 后端
  • Spring
  • 框架
轩辕李
2023-05-18
目录

Spring容器初始化过程和Bean生命周期探究

# 一、前言

作为Java开发者,你肯定遇到过这样的问题:为什么有时候@Autowired注入的Bean是null?为什么有些Bean没有按预期的顺序初始化?这些问题的根源往往在于对Spring容器初始化过程和Bean生命周期的理解不够深入。

Spring容器就像一个精密的工厂,它不仅要生产出各种Bean,还要管理它们的整个生命历程。从出生(实例化)到成长(初始化)再到最终的消亡(销毁),每个环节都有着严格的规则和扩展点。

理解这套机制不仅能让你写出更可靠的代码,还能在出现问题时快速定位原因。本文将带你深入Spring的"生产车间",看看容器是如何启动的,Bean又是如何一步步"成长"的。

# 二、容器基础

想要理解Spring容器,我们先得知道它的基本组成部分。就像要理解一家工厂是怎么运作的,得先了解车间、生产线这些基础设施一样。

# 1、- 最基础的容器

BeanFactory是Spring的"祖师爷",它定义了最基本的容器功能。你可以把它想象成一个最简单的工厂,主要负责:

  • 生产Bean:根据配置信息,用反射创建Bean实例
  • 管理依赖:自动把Bean需要的依赖注入进去
  • 控制生命周期:决定Bean什么时候创建,什么时候销毁
  • 作用域管理:单例的Bean只创建一次,原型的Bean每次都新建

不过说实话,直接使用BeanFactory的场景不多,它更像是一个接口规范。在实际项目中,我们用得更多的是它的"升级版"——ApplicationContext。

# 2、- 功能齐全的容器

ApplicationContext才是我们日常开发的主角,它在BeanFactory的基础上增加了很多实用功能:

  • 事件发布:可以发布和监听应用事件,比如容器启动完成事件
  • 国际化支持:支持多语言消息,让应用国际化变得简单
  • 资源加载:可以加载各种资源文件,如配置文件、图片等
  • 环境抽象:统一管理不同环境的配置,如dev、test、prod
  • 层级结构:支持父子容器,在复杂应用中很有用

简单来说,如果BeanFactory是毛坯房,那ApplicationContext就是精装修,该有的都有了。这也是为什么我们在Spring Boot项目中直接使用的就是它。

# 3、Aware接口 - Bean获取容器资源的后门

有时候,你的Bean需要知道一些容器的信息,比如自己的名字、所在的容器等等。Spring提供了一堆以Aware结尾的接口来满足这些需求,就像给Bean开了个"后门",让它们能拿到容器的一些内部信息。

常用的几个接口:

  • ApplicationContextAware:最常用的,能拿到整个应用上下文
  • BeanNameAware:让Bean知道自己在容器中的名字
  • EnvironmentAware:获取环境配置,比如配置文件中的属性
  • ResourceLoaderAware:获取资源加载器,用来加载文件等资源

这些接口的用法都很简单,实现接口,Spring在初始化Bean时会自动调用对应的set方法。举个例子:

@Component
public class MyService implements ApplicationContextAware {
    
    private ApplicationContext context;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.context = applicationContext;
    }
    
    public void dynamicGetBean() {
        // 现在可以动态获取其他Bean了
        UserService userService = context.getBean(UserService.class);
        userService.processUser();
    }
}

# 三、容器启动流程

当你启动一个Spring应用时,Spring容器经历了一个复杂的"开机"过程。这个过程的核心就是refresh()方法,它就像是启动按钮,一旦按下,整个容器就开始运转起来。

我们先看一个简单的启动示例:

public class Application {
    public static void main(String[] args) {
        // 创建容器
        AnnotationConfigApplicationContext context = 
            new AnnotationConfigApplicationContext();
        
        // 注册配置类    
        context.register(AppConfig.class);
        
        // 这一步是关键 - 启动容器
        context.refresh();
        
        // 现在可以使用Bean了
        UserService userService = context.getBean(UserService.class);
        userService.processUser();
        
        // 别忘了关闭容器
        context.close();
    }
}

# 1、refresh()方法 - 容器启动的十二个步骤

这个方法就是容器启动的"总导演",它按照固定的顺序执行了12个关键步骤:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 第1步:准备工作 - 记录启动时间,设置标志位
        prepareRefresh();

        // 第2步:获取BeanFactory - 这是Bean的生产车间
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 第3步:配置BeanFactory - 设置类加载器、添加后置处理器
        prepareBeanFactory(beanFactory);

        try {
            // 第4步:子类可以对BeanFactory进行后置处理
            postProcessBeanFactory(beanFactory);

            // 第5步:执行BeanFactory后置处理器 - 修改Bean定义
            invokeBeanFactoryPostProcessors(beanFactory);

            // 第6步:注册Bean后置处理器 - 拦截Bean创建过程
            registerBeanPostProcessors(beanFactory);

            // 第7步:初始化消息源 - 国际化支持
            initMessageSource();

            // 第8步:初始化事件广播器 - 事件发布功能
            initApplicationEventMulticaster();

            // 第9步:子类可以初始化特殊Bean - 如Web容器的Servlet
            onRefresh();

            // 第10步:注册事件监听器
            registerListeners();

            // 第11步:实例化所有非懒加载的单例Bean - 这是重点!
            finishBeanFactoryInitialization(beanFactory);

            // 第12步:完成刷新 - 发布容器刷新完成事件
            finishRefresh();
        } catch (BeansException ex) {
            // 出错了就清理现场
            destroyBeans();
            cancelRefresh(ex);
            throw ex;
        } finally {
            // 清理缓存
            resetCommonCaches();
        }
    }
}

其中最关键的两个概念你必须要理解:

  • BeanFactoryPostProcessor:在Bean实例化之前,修改Bean的定义信息。比如修改Bean的属性值、作用域等
  • BeanPostProcessor:在Bean实例化之后,对Bean进行处理。比如创建代理对象、注入依赖等

这两个处理器就是Spring扩展能力的核心,后面我们会详细介绍。

# 四、Bean的一生

一个Bean从诞生到消亡,就像人的一生一样,经历了不同的阶段。理解这个过程,能帮你解决很多实际开发中的问题。

# 1、Bean生命周期的完整流程

想象Bean的成长过程:

1. 出生阶段

  • 实例化:通过构造函数创建Bean对象
  • 属性填充:通过@Autowired等方式注入依赖

2. 成长阶段

  • Aware接口回调:让Bean知道自己的身份信息
    • BeanNameAware:告诉Bean自己叫什么名字
    • ApplicationContextAware:告诉Bean自己在哪个容器里
  • BeanPostProcessor前置处理:类似"入学体检"
  • 初始化方法调用:
    • @PostConstruct注解的方法
    • InitializingBean.afterPropertiesSet()
    • 自定义的init-method
  • BeanPostProcessor后置处理:类似"毕业典礼"

3. 工作阶段

  • Bean正式投入使用,处理业务逻辑

4. 退休阶段(容器关闭时)

  • @PreDestroy注解的方法
  • DisposableBean.destroy()
  • 自定义的destroy-method

这个流程看起来复杂,但记住关键的几个节点就够了:属性注入 → Aware接口 → 初始化方法 → 可以使用 → 销毁方法。

# 2、生命周期扩展点

Spring在Bean的生命周期中提供了很多扩展点,让你可以在合适的时机插入自己的逻辑:

容器级别的扩展点:

  • BeanFactoryPostProcessor:修改Bean定义,比如修改属性值、添加新的Bean定义
  • BeanPostProcessor:拦截Bean创建过程,可以创建代理、添加功能等

Bean级别的扩展点:

  • Aware接口:让Bean获取容器信息
  • InitializingBean/@PostConstruct:Bean初始化时执行逻辑
  • DisposableBean/@PreDestroy:Bean销毁时清理资源
  • Lifecycle接口:控制Bean的启动和停止

实际开发中,最常用的就是@PostConstruct和@PreDestroy注解,简单方便。

# 五、- 容器启动时的"总工程师"

BeanFactoryPostProcessor是Spring提供的一个非常强大的扩展点,它能在Bean实例化之前修改Bean的定义信息。就像工厂的总工程师,在生产线开始生产之前,可以修改生产图纸。

# 1、Spring内置的BeanFactoryPostProcessor

Spring自带了几个常用的处理器:

  • ConfigurationClassPostProcessor:处理@Configuration类,解析@Bean、@ComponentScan等注解
  • PropertySourcesPlaceholderConfigurer:处理${}占位符,用配置文件的值替换
  • AutowiredAnnotationBeanPostProcessor:处理@Autowired注解

其中最重要的是ConfigurationClassPostProcessor,它负责处理基于注解的配置。

# 2、实战案例:MyBatis如何注册Mapper

我们来看一个经典的例子:MyBatis是如何把Mapper接口注册到Spring容器中的。你有没有想过,为什么一个接口可以直接@Autowired注入使用?

MyBatis提供了MapperScannerConfigurer,它实现了BeanDefinitionRegistryPostProcessor接口:

@Component
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor {
    
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        // 扫描指定包下的Mapper接口
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setBasePackage("com.example.mapper");
        
        // 为每个Mapper接口创建BeanDefinition
        // 并设置FactoryBean为MapperFactoryBean
        scanner.scan();
    }
}

这个过程的巧妙之处在于:

  1. 扫描阶段:找到所有Mapper接口
  2. 注册阶段:为每个接口创建一个BeanDefinition
  3. 代理创建:使用MapperFactoryBean创建接口的代理实现

这样,原本不能实例化的接口,就变成了可以注入的Bean。这就是BeanFactoryPostProcessor的威力。

上面提到的BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的增强版,它不仅能修改Bean定义,还能向容器中添加新的Bean定义。

执行顺序是:BeanDefinitionRegistryPostProcessor → BeanFactoryPostProcessor → Bean实例化

这样设计的好处是,先注册新的Bean定义,再统一处理所有Bean定义,最后才开始实例化。

# 3、- 特殊的Bean工厂

FactoryBean是Spring的一个特殊接口,它不是普通的Bean,而是专门用来创建其他Bean的工厂。当Spring遇到实现了FactoryBean接口的类时,它会调用该工厂的getObject()方法来获取真正的Bean。

public class UserFactoryBean implements FactoryBean<User> {
    
    @Override
    public User getObject() throws Exception {
        // 复杂的创建逻辑
        User user = new User();
        user.setName("Generated User");
        return user;
    }
    
    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
    
    @Override
    public boolean isSingleton() {
        return true;
    }
}

这种机制特别适用于创建复杂对象,比如MyBatis的MapperFactoryBean就是用来创建Mapper接口的代理对象的。

简单来说,ClassPathBeanDefinitionScanner就是Spring用来扫描类路径的工具,它能找到所有带@Component等注解的类,并把它们注册成Bean定义。

BeanDefinition则是Bean的"身份证",记录了Bean的所有信息:类名、作用域、属性值、依赖关系等。Spring就是根据这些信息来创建Bean实例的。

# 六、- Bean创建过程的“参谋”

如果说BeanFactoryPostProcessor是在Bean实例化前修改定义,那BeanPostProcessor就是在Bean实例化后进行加工。它就像是一个“参谋”,在Bean的创建过程中提供建议和修改。

# 1、Spring内置的BeanPostProcessor

Spring自带了很多实用的处理器:

  • AutowiredAnnotationBeanPostProcessor:处理@Autowired、@Value注觢
  • CommonAnnotationBeanPostProcessor:处理@PostConstruct、@PreDestroy、@Resource注觢
  • ApplicationListenerDetector:检测事件监听器并自动注册
  • AsyncAnnotationBeanPostProcessor:处理@Async注觢

这些处理器就是为什么你在Bean中使用@Autowired、@PostConstruct等注觢就能起作用的原因。

# 2、自定义BeanPostProcessor

你也可以写自己的BeanPostProcessor,来实现一些特殊的需求。比如记录Bean的创建日志:

@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if (bean.getClass().isAnnotationPresent(Service.class)) {
            System.out.println("Service Bean [" + beanName + "] 即将初始化");
        }
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean.getClass().isAnnotationPresent(Service.class)) {
            System.out.println("Service Bean [" + beanName + "] 初始化完成");
        }
        return bean;
    }
}

这样,所有的Service Bean在初始化前后都会打印日志。这对于调试和监控Bean的创建过程很有帮助。

# 七、初始化和销毁方法

在实际开发中,我们经常需要在Bean初始化时做一些准备工作,比如加载配置、初始化连接池等;在Bean销毁时做一些清理工作,比如关闭连接、释放资源等。

# 1、使用注解方式(推荐)

最简单的方式就是使用@PostConstruct和@PreDestroy注觢:

@Service
public class UserService {
    
    @PostConstruct
    public void init() {
        System.out.println("初始化用户服务,加载配置文件...");
        // 初始化逻辑
    }
    
    @PreDestroy
    public void cleanup() {
        System.out.println("清理用户服务资源...");
        // 清理逻辑
    }
    
    public void processUser() {
        System.out.println("处理用户业务");
    }
}

# 2、使用接口方式

如果你不喜欢注觢,也可以实现接口:

@Service
public class DataService implements InitializingBean, DisposableBean {
    
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("数据服务初始化完成");
    }
    
    @Override
    public void destroy() throws Exception {
        System.out.println("数据服务销毁");
    }
}

注意:这些方法只对单例模式的Bean有效。对于原型模式(prototype)的Bean,Spring不会管理其销毁,所以@PreDestroy不会被调用。

# 八、Lifecycle接口 - 控制Bean的启停

有时候,我们需要更精细地控制Bean的启动和停止行为,比如后台任务、线程池、网络服务等。这时候就可以使用Lifecycle接口。

# 1、实现示例

来看一个后台任务的例子:

@Component
public class BackgroundTask implements Lifecycle {

    private boolean running = false;

    @Override
    public void start() {
        if (!running) {
            System.out.println("BackgroundTask started!");
            running = true;
            // 启动后台任务
            new Thread(this::runTask).start();
        }
    }

    @Override
    public void stop() {
        if (running) {
            System.out.println("BackgroundTask stopped!");
            running = false;
            // 停止后台任务
        }
    }

    @Override
    public boolean isRunning() {
        return running;
    }

    private void runTask() {
        while (running) {
            System.out.println("BackgroundTask is running...");
            try {
                Thread.sleep(1000); // 模拟任务执行
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

这样,当你调用context.start()时,后台任务就会启动;调用context.stop()时,任务就会停止。

# 2、Lifecycle扩展接口

# 2.1、SmartLifecycle

SmartLifecycle 是 Lifecycle 的扩展接口,提供了更丰富的生命周期控制功能,例如:

  • 自动启动:在 Spring 容器启动时自动调用 start() 方法。
  • 阶段控制:通过 getPhase() 方法定义组件的启动和停止顺序。
# a、示例代码
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;

@Component
public class SmartBackgroundTask implements SmartLifecycle {

    private boolean running = false;

    @Override
    public void start() {
        if (!running) {
            System.out.println("SmartBackgroundTask started!");
            running = true;
            new Thread(this::runTask).start();
        }
    }

    @Override
    public void stop() {
        if (running) {
            System.out.println("SmartBackgroundTask stopped!");
            running = false;
        }
    }

    @Override
    public boolean isRunning() {
        return running;
    }

    @Override
    public boolean isAutoStartup() {
        return true; // 自动启动
    }

    @Override
    public void stop(Runnable callback) {
        stop();
        callback.run(); // 通知容器停止完成
    }

    @Override
    public int getPhase() {
        return 0; // 定义启动和停止的顺序
    }

    private void runTask() {
        while (running) {
            System.out.println("SmartBackgroundTask is running...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
}

# 2.2、Phased接口

Phased 接口定义了 getPhase() 方法,用于控制组件的启动和停止顺序。SmartLifecycle 已经继承了 Phased。

# 3、Lifecycle与其他生命周期方式的区别

  • Lifecycle:用于显式控制组件的启动和停止行为,通常用于需要长时间运行的任务或资源管理。
  • @PostConstruct 和 @PreDestroy:用于在 Bean 初始化和销毁时执行特定的方法,通常用于简单的初始化和清理操作。

# 九、总结

通过本文的探索,我们深入了解了Spring容器的初始化过程和Bean的生命周期。虽然这些机制看起来复杂,但理解它们的核心原理后,你会发现Spring的设计其实非常优雅。

掌握了这些知识,你就能:

  • 在Bean注入失败时快速定位问题
  • 灵活运用各种扩展点来实现自定义功能
  • 更好地理解Spring Boot的自动配置原理
  • 写出更健壮、更可靠的Spring应用

记住,Spring的核心思想就是控制反转和依赖注入,而容器和Bean生命周期机制则是这一思想的完美体现。希望这篇文章能帮助你在Spring开发的道路上走得更远!

祝你变得更强!

编辑 (opens new window)
#Spring容器初始化#Spring Bean生命周期
上次更新: 2025/08/16
Reactor 3指南
Spring容器:从依赖管理到注解配置全解析

← Reactor 3指南 Spring容器:从依赖管理到注解配置全解析→

最近更新
01
AI时代的编程心得
09-11
02
Claude Code与Codex的协同工作
09-01
03
Claude Code实战之供应商切换工具
08-18
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式