首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Spring 容器启动的 10 大核心步骤:从源码到实战的深度剖析

Spring 容器启动的 10 大核心步骤:从源码到实战的深度剖析

作者头像
果酱带你啃java
发布2026-04-14 12:14:54
发布2026-04-14 12:14:54
590
举报

一、引言:Spring 容器 ——Java 应用的 "神经中枢"

在 Java 开发的世界里,Spring 框架无疑是最耀眼的明星,而 Spring 容器则是这颗明星的核心。作为整个应用的 "神经中枢",Spring 容器负责管理 Bean 的生命周期、处理依赖注入、协调各组件之间的交互,支撑着应用的正常运行。

然而,对于许多开发者而言,Spring 容器的启动过程如同一个 "黑箱"—— 我们知道如何配置 Bean,如何使用依赖注入,却对容器从初始化到最终可用经历了哪些步骤知之甚少。这种 "知其然不知其所以然" 的状态,不仅会在遇到问题时束手无策,更会限制我们对 Spring 高级特性的理解和运用。

本文将带您深入 Spring 容器的内部世界,通过源码分析和实战示例,详细拆解容器启动的 10 大核心步骤。无论您是希望提升 Spring 功底的中级开发者,还是追求知根知底的资深工程师,都能从本文中获得对 Spring 容器的全新认知。

二、Spring 容器的基石:核心接口与实现类

在深入分析启动流程之前,我们需要先了解 Spring 容器的核心接口和实现类,这是理解后续流程的基础。

2.1 核心接口层次

Spring 容器的核心接口主要有两个:BeanFactoryApplicationContext

  • BeanFactory是 Spring 容器的最基础接口,定义了 Bean 管理的基本功能,如获取 Bean、判断 Bean 是否存在等。
  • ApplicationContext继承自BeanFactory,是更完整的容器接口,除了BeanFactory的功能外,还提供了国际化、事件发布、资源加载等增强功能。

它们的核心关系如下:

BeanFactory HierarchicalBeanFactory ConfigurableBeanFactory ApplicationContext ConfigurableApplicationContext

2.2 常用实现类

在实际开发中,我们常用的容器实现类有:

  • ClassPathXmlApplicationContext从类路径加载 XML 配置文件的容器
  • FileSystemXmlApplicationContext从文件系统加载 XML 配置文件的容器
  • AnnotationConfigApplicationContext基于注解配置的容器(Spring 3.0+)
  • WebApplicationContextWeb 应用专用的容器,如 XmlWebApplicationContext

本文将以AnnotationConfigApplicationContext为例进行分析,因为它是现代 Spring 应用(尤其是 Spring Boot)中最常用的容器实现。

三、Spring 容器启动的 10 大核心步骤

Spring 容器的启动过程看似复杂,实则可以分解为 10 个核心步骤。我们将逐一剖析每个步骤的具体操作和源码实现。

3.1 步骤 1:初始化容器实例

容器启动的第一步是创建ApplicationContext实例。以AnnotationConfigApplicationContext为例,这一步会初始化容器的基本组件,如 Bean 定义读取器和类路径扫描器。

示例代码

代码语言:javascript
复制
@Slf4j
public class ContainerStartupDemo {
    public static void main(String[] args) {
        // 步骤1:初始化容器实例
        log.info("开始初始化AnnotationConfigApplicationContext");
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        log.info("AnnotationConfigApplicationContext实例创建完成");
    }
}

源码解析(简化版):

代码语言:javascript
复制
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
    private final AnnotatedBeanDefinitionReader reader;
    private final ClassPathBeanDefinitionScanner scanner;

    public AnnotationConfigApplicationContext() {
        // 创建Bean定义读取器,用于读取注解标注的类
        this.reader = new AnnotatedBeanDefinitionReader(this);
        // 创建类路径扫描器,用于扫描指定包下的Bean
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }
}

核心操作

  • 初始化AnnotatedBeanDefinitionReader:负责将注解标注的类(如@Configuration@Service)转换为BeanDefinition
  • 初始化ClassPathBeanDefinitionScanner:负责扫描指定包路径,发现并注册 Bean

3.2 步骤 2:设置配置源

容器实例创建后,需要指定配置源 —— 告诉容器从哪里加载 Bean 定义。配置源可以是@Configuration标注的配置类、包路径或 XML 文件等。

示例代码

代码语言:javascript
复制
@Configuration
@ComponentScan(basePackages = "com.example.demo.service")
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
}

@Service
@Slf4j
public class OrderService {
    public void createOrder() {
        log.info("创建订单成功");
    }
}

@Slf4j
public class ContainerStartupDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        // 步骤2:设置配置源(可以是配置类、包路径等)
        log.info("设置配置源");
        context.register(AppConfig.class); // 注册配置类
        // 或使用扫描包路径:context.scan("com.example.demo");

        context.refresh(); // 后续步骤将在refresh()方法中执行
    }
}

核心操作

  • 通过register()方法注册配置类
  • 或通过scan()方法指定需要扫描的包路径
  • 配置源是容器获取 Bean 定义的基础

3.3 步骤 3:刷新容器 —— 启动的核心入口

refresh()方法是容器启动的核心入口,几乎所有关键步骤都在这个方法中完成。它定义在ConfigurableApplicationContext接口中,由具体实现类完成。

源码解析(简化版):

代码语言:javascript
复制
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 准备刷新
        prepareRefresh();

        // 获取BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 准备BeanFactory
        prepareBeanFactory(beanFactory);

        try {
            // 后置处理BeanFactory
            postProcessBeanFactory(beanFactory);

            // 执行BeanFactory后置处理器
            invokeBeanFactoryPostProcessors(beanFactory);

            // 注册Bean后置处理器
            registerBeanPostProcessors(beanFactory);

            // 初始化消息源
            initMessageSource();

            // 初始化事件多播器
            initApplicationEventMulticaster();

            // 初始化特定上下文的Bean
            onRefresh();

            // 注册事件监听器
            registerListeners();

            // 实例化所有非懒加载单例Bean
            finishBeanFactoryInitialization(beanFactory);

            // 完成刷新
            finishRefresh();
        }
        // 异常处理...
    }
}

核心操作

  • refresh()方法是一个模板方法,定义了容器启动的整体流程
  • 所有关键步骤都在这个方法中按顺序执行
  • 使用同步锁确保容器刷新过程的线程安全

3.4 步骤 4:准备刷新 —— 初始化环境与验证

prepareRefresh()方法负责在容器刷新前进行一些准备工作,包括初始化环境、验证必要的属性等。

源码解析(简化版):

代码语言:javascript
复制
protected void prepareRefresh() {
    // 记录启动时间和活跃状态
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);

    // 初始化环境(如.property文件、环境变量等)
    initPropertySources();

    // 验证所有必要的属性都已设置
    getEnvironment().validateRequiredProperties();

    // 初始化早期事件集合
    if (this.earlyApplicationEvents == null) {
        this.earlyApplicationEvents = new LinkedHashSet<>();
    }
}

示例代码

代码语言:javascript
复制
@Configuration
@PropertySource("classpath:app.properties") // 加载配置文件
public class AppConfig {
    @Value("${app.name}")
    private String appName;

    @Bean
    public UserService userService() {
        return new UserService(appName);
    }
}

// app.properties
app.name=demo-app
app.required.property=test // 必要属性

@Slf4j
public class UserService {
    private final String appName;

    public UserService(String appName) {
        this.appName = appName;
    }

    public void printAppName() {
        log.info("当前应用名称:{}", appName);
    }
}

核心操作

  • 初始化环境变量和属性源
  • 验证必要的属性是否存在(通过@RequiredvalidateRequiredProperties()
  • 为后续步骤准备基础环境

3.5 步骤 5:获取 BeanFactory—— 创建或刷新 Bean 工厂

obtainFreshBeanFactory()方法负责获取一个新鲜的BeanFactory,对于AnnotationConfigApplicationContext来说,会创建一个DefaultListableBeanFactory实例。

源码解析(简化版):

代码语言:javascript
复制
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 刷新BeanFactory
    refreshBeanFactory();
    // 获取刷新后的BeanFactory
    return getBeanFactory();
}

protected final void refreshBeanFactory() throws IllegalStateException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 创建新的BeanFactory实例
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        // 定制BeanFactory(如设置是否允许循环引用、是否允许覆盖Bean定义等)
        customizeBeanFactory(beanFactory);
        // 加载Bean定义
        loadBeanDefinitions(beanFactory);
        this.beanFactory = beanFactory;
    }
    // 异常处理...
}

核心操作

  • 创建DefaultListableBeanFactory实例(Spring 默认的 Bean 工厂实现)
  • 配置 Bean 工厂的行为(如是否允许 Bean 定义覆盖、是否允许循环引用)
  • 加载 Bean 定义到 Bean 工厂中

3.6 步骤 6:准备 BeanFactory—— 配置 Bean 工厂的标准组件

prepareBeanFactory()方法负责配置 BeanFactory 的标准组件,如类加载器、表达式解析器、属性编辑器等。

源码解析(简化版):

代码语言:javascript
复制
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 设置类加载器
    beanFactory.setBeanClassLoader(getClassLoader());
    // 设置表达式解析器
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    // 添加属性编辑器注册表
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // 添加Bean后置处理器:ApplicationContextAwareProcessor
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

    // 设置忽略自动装配的接口
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    // ...忽略其他Aware接口

    // 注册默认的依赖解析
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // 添加Bean后置处理器:ApplicationListenerDetector
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    // 注册加载时织入的AspectJ支持
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // 注册默认的环境Bean
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    // 注册其他默认Bean...
}

核心操作

  • 配置 BeanFactory 的基础组件(类加载器、表达式解析器等)
  • 注册ApplicationContextAwareProcessor,使 Bean 能感知ApplicationContext
  • 忽略特定接口的自动装配(如EnvironmentAware,它们通过其他方式注入)
  • 注册默认的依赖解析器和 Bean

3.7 步骤 7:执行 BeanFactory 后置处理器 —— 扩展 Bean 定义

invokeBeanFactoryPostProcessors()方法负责执行所有注册的BeanFactoryPostProcessor,这些处理器可以修改或增加 Bean 定义。

示例代码

代码语言:javascript
复制
// 自定义BeanFactoryPostProcessor:修改Bean定义
@Component
@Slf4j
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        log.info("执行CustomBeanFactoryPostProcessor");

        // 获取UserService的Bean定义
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");

        // 修改Bean定义:设置初始化方法
        beanDefinition.setInitMethodName("init");

        // 添加属性
        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
        propertyValues.add("version", "1.0.0");
    }
}

@Service
@Slf4j
public class UserService {
    private String version;

    public void setVersion(String version) {
        this.version = version;
    }

    public void init() {
        log.info("UserService初始化,版本:{}", version);
    }
}

源码解析(核心逻辑):

代码语言:javascript
复制
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // 委托给PostProcessorRegistrationDelegate执行BeanFactoryPostProcessor
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    // 检查是否有LoadTimeWeaver,如果有则需要当前上下文的类加载器
    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}

核心操作

  • 执行所有BeanFactoryPostProcessor,包括:
    • BeanDefinitionRegistryPostProcessor可以注册新的 Bean 定义
    • BeanFactoryPostProcessor可以修改已有的 Bean 定义
  • 这是 Spring 提供的重要扩展点,允许在 Bean 实例化前修改 Bean 定义

3.8 步骤 8:注册 Bean 后置处理器 —— 增强 Bean 实例化过程

registerBeanPostProcessors()方法负责注册所有BeanPostProcessor,这些处理器可以在 Bean 实例化的各个阶段对 Bean 进行增强。

示例代码

代码语言:javascript
复制
// 自定义BeanPostProcessor:在Bean初始化前后添加逻辑
@Component
@Slf4j
public class CustomBeanPostProcessor implements BeanPostProcessor {
    // Bean初始化前执行
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("userService".equals(beanName)) {
            log.info("CustomBeanPostProcessor - 初始化前:{}", beanName);
        }
        return bean;
    }

    // Bean初始化后执行
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if ("userService".equals(beanName)) {
            log.info("CustomBeanPostProcessor - 初始化后:{}", beanName);
        }
        return bean;
    }
}

源码解析(核心逻辑):

代码语言:javascript
复制
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // 委托给PostProcessorRegistrationDelegate注册BeanPostProcessor
    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

核心操作

  • 注册所有BeanPostProcessor,并按优先级排序
  • BeanPostProcessor可以在 Bean 初始化前后、依赖注入前后等阶段对 Bean 进行增强
  • 常见的BeanPostProcessor包括:AutowiredAnnotationBeanPostProcessor(处理 @Autowired)、RequiredAnnotationBeanPostProcessor(处理 @Required)等

3.9 步骤 9:初始化消息源和事件机制

这一步包括初始化消息源(用于国际化)和事件多播器(用于事件发布与监听),是 Spring 容器的重要基础设施。

9.1 初始化消息源(initMessageSource ())

源码解析

代码语言:javascript
复制
protected void initMessageSource() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();

    // 如果存在名为"messageSource"的Bean,则使用它作为消息源
    if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
        this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
        if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
            HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
            if (hms.getParentMessageSource() == null) {
                hms.setParentMessageSource(getInternalParentMessageSource());
            }
        }
    } else {
        // 否则创建默认的消息源
        DelegatingMessageSource dms = new DelegatingMessageSource();
        dms.setParentMessageSource(getInternalParentMessageSource());
        this.messageSource = dms;
        beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
    }
}

示例代码

代码语言:javascript
复制
protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();

    // 如果存在名为"applicationEventMulticaster"的Bean,则使用它
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
    } else {
        // 否则创建默认的SimpleApplicationEventMulticaster
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
    }
}
9.2 初始化事件多播器(initApplicationEventMulticaster ())

源码解析

代码语言:javascript
复制
protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();

    // 如果存在名为"applicationEventMulticaster"的Bean,则使用它
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
    } else {
        // 否则创建默认的SimpleApplicationEventMulticaster
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
    }
}

示例代码

代码语言:javascript
复制
// 自定义事件
public class OrderCreatedEvent extends ApplicationEvent {
    private final Long orderId;

    public OrderCreatedEvent(Object source, Long orderId) {
        super(source);
        this.orderId = orderId;
    }

    public Long getOrderId() {
        return orderId;
    }
}

// 事件发布者
@Service
public class OrderService {
    @Autowired
    private ApplicationEventPublisher eventPublisher;

    public void createOrder(Long orderId) {
        // 创建订单逻辑...
        // 发布事件
        eventPublisher.publishEvent(new OrderCreatedEvent(this, orderId));
    }
}

// 事件监听器
@Component
@Slf4j
public class OrderEventListener {
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        log.info("收到订单创建事件,订单ID:{}", event.getOrderId());
        // 处理事件:如发送通知、更新库存等
    }
}

核心操作

初始化消息源,支持国际化消息

  • 初始化事件多播器,支持事件的发布与监听
  • 这两个组件是 Spring 容器的重要扩展点,增强了应用的灵活性

3.10 步骤 10:实例化非懒加载单例 Bean

finishBeanFactoryInitialization()方法负责实例化所有非懒加载的单例 Bean,这是容器启动过程中最消耗资源的步骤之一。

源码解析(核心逻辑):

代码语言:javascript
复制
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // 初始化ConversionService(类型转换服务)
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
            beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
        beanFactory.setConversionService(
                beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    }

    // 注册默认的属性编辑器
    if (!beanFactory.hasEmbeddedValueResolver()) {
        beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
    }

    // 实例化LoadTimeWeaverAware类型的Bean
    String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
    for (String weaverAwareName : weaverAwareNames) {
        getBean(weaverAwareName);
    }

    // 冻结Bean定义,防止后续修改
    beanFactory.freezeConfiguration();

    // 实例化所有非懒加载的单例Bean
    beanFactory.preInstantiateSingletons();
}

Bean 实例化的核心流程

创建 Bean 实例

  1. 通过构造器或工厂方法创建 Bean 对象
  2. 属性注入为 Bean 的属性注入依赖(如 @Autowired、@Resource)
  3. 调用 Aware 接口BeanNameAwareApplicationContextAware
  4. BeanPostProcessor 前置处理调用postProcessBeforeInitialization()
  5. 初始化方法调用@PostConstruct注解的方法或init-method指定的方法
  6. BeanPostProcessor 后置处理调用postProcessAfterInitialization()(AOP 代理通常在此处创建)
  7. 注册销毁方法为单例 Bean 注册销毁回调

示例代码

代码语言:javascript
复制
@Service
@Slf4j
public class ProductService implements BeanNameAware {
    private String beanName;

    // 构造器
    public ProductService() {
        log.info("ProductService构造器执行");
    }

    @Autowired
    private CategoryService categoryService;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        log.info("BeanNameAware.setBeanName:{}", name);
    }

    @PostConstruct
    public void init() {
        log.info("PostConstruct初始化方法执行");
    }

    public void queryProduct(Long productId) {
        log.info("查询商品,ID:{}", productId);
    }

    @PreDestroy
    public void destroy() {
        log.info("PreDestroy销毁方法执行");
    }
}

执行顺序输出

代码语言:javascript
复制
ProductService构造器执行
BeanNameAware.setBeanName:productService
PostConstruct初始化方法执行

3.11 步骤 11:完成刷新 —— 发布容器就绪事件

finishRefresh()方法是容器启动的最后一步,主要负责发布容器就绪事件、初始化生命周期处理器等。

源码解析(简化版):

代码语言:javascript
复制
protected void finishRefresh() {
    // 初始化生命周期处理器
    initLifecycleProcessor();

    // 启动所有实现Lifecycle接口的Bean
    getLifecycleProcessor().onRefresh();

    // 发布ContextRefreshedEvent事件
    publishEvent(new ContextRefreshedEvent(this));

    // 向MBeanServer注册可管理的Bean(如果开启了JMX)
    if (this.registerShutdownHook) {
        registerShutdownHook();
    }
}

示例代码

代码语言:javascript
复制
// 监听容器刷新完成事件
@Component
@Slf4j
public class ContextRefreshListener {
    @EventListener(ContextRefreshedEvent.class)
    public void onContextRefreshed(ContextRefreshedEvent event) {
        log.info("Spring容器刷新完成,所有Bean已初始化");
        ApplicationContext context = event.getApplicationContext();

        // 容器就绪后执行一些初始化操作
        UserService userService = context.getBean(UserService.class);
        userService.initData();
    }
}

@Service
@Slf4j
public class UserService {
    public void initData() {
        log.info("执行初始化数据操作");
    }
}

核心操作

初始化生命周期处理器,管理 Bean 的生命周期

  • 发布ContextRefreshedEvent事件,通知所有监听器容器已就绪
  • 注册关闭钩子,确保容器关闭时能正确销毁 Bean

四、Spring 容器启动流程全景图

为了更直观地理解 Spring 容器的启动流程,我们将上述步骤整理为一个全景图:

容器启动流程 ├── 1. 初始化容器实例(new AnnotationConfigApplicationContext()) │ └── 创建AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner ├── 2. 设置配置源(register()或scan()) │ └── 指定配置类或扫描包路径 ├── 3. 刷新容器(refresh()) │ ├── 4. 准备刷新(prepareRefresh()) │ │ ├── 初始化环境变量 │ │ └── 验证必要属性 │ ├── 5. 获取BeanFactory(obtainFreshBeanFactory()) │ │ ├── 创建DefaultListableBeanFactory │ │ └── 加载Bean定义 │ ├── 6. 准备BeanFactory(prepareBeanFactory()) │ │ ├── 配置类加载器、表达式解析器等 │ │ └── 注册ApplicationContextAwareProcessor等 │ ├── 7. 执行BeanFactory后置处理器(invokeBeanFactoryPostProcessors()) │ │ ├── 执行BeanDefinitionRegistryPostProcessor │ │ └── 执行BeanFactoryPostProcessor │ ├── 8. 注册Bean后置处理器(registerBeanPostProcessors()) │ │ └── 按优先级注册所有BeanPostProcessor │ ├── 9. 初始化消息源和事件机制 │ │ ├── 初始化消息源(initMessageSource()) │ │ └── 初始化事件多播器(initApplicationEventMulticaster()) │ ├── 10. 实例化非懒加载单例Bean(finishBeanFactoryInitialization()) │ │ ├── 冻结Bean定义 │ │ └── 按流程实例化所有非懒加载单例Bean │ └── 11. 完成刷新(finishRefresh()) │ ├── 初始化生命周期处理器 │ ├── 发布ContextRefreshedEvent事件 │ └── 注册关闭钩子 └── 容器启动完成,可用于获取Bean

五、实战分析:追踪容器启动过程

为了加深对容器启动流程的理解,我们通过一个完整的示例来追踪容器的启动过程。

完整示例代码

  1. 配置类:
代码语言:javascript
复制
@Configuration
@ComponentScan(basePackages = "com.example.demo")
public class AppConfig {
}
  1. 服务类:
代码语言:javascript
复制
@Service
@Slf4j
public class DemoService {
    @PostConstruct
    public void init() {
        log.info("DemoService初始化完成");
    }
}
  1. 事件监听器:
代码语言:javascript
复制
@Component
@Slf4j
public class StartupListener {
    @EventListener(ContextRefreshedEvent.class)
    public void onStartup(ContextRefreshedEvent event) {
        log.info("容器启动完成,Bean数量:{}", event.getApplicationContext().getBeanDefinitionCount());
    }
}
  1. 启动类:
代码语言:javascript
复制
@Slf4j
public class ContainerTrackingDemo {
    public static void main(String[] args) {
        log.info("开始启动Spring容器");

        // 步骤1:初始化容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        log.info("容器实例创建完成");

        // 步骤2:设置配置源
        context.register(AppConfig.class);
        log.info("配置源注册完成");

        // 步骤3:刷新容器(触发后续所有步骤)
        log.info("开始刷新容器");
        context.refresh();
        log.info("容器刷新完成");

        // 使用容器
        DemoService demoService = context.getBean(DemoService.class);
        log.info("获取到DemoService:{}", demoService);

        // 关闭容器
        context.close();
    }
}

执行结果及流程分析

开始启动Spring容器 容器实例创建完成 配置源注册完成 开始刷新容器 // 步骤4:准备刷新 // 步骤5:获取BeanFactory // 步骤6:准备BeanFactory // 步骤7:执行BeanFactory后置处理器 // 步骤8:注册Bean后置处理器 // 步骤9:初始化消息源和事件机制 // 步骤10:实例化非懒加载单例Bean DemoService初始化完成 // 步骤11:完成刷新 容器启动完成,Bean数量:10 容器刷新完成 获取到DemoService:com.example.demo.service.DemoService@123456

通过这个示例,我们可以清晰地看到容器启动的各个阶段,并验证了之前介绍的核心步骤。

六、容器启动的性能优化策略

Spring 容器的启动性能对应用的整体性能有重要影响,尤其是在大型应用中。以下是一些常用的优化策略:

6.1 减少扫描范围

@ComponentScan默认会扫描指定包及其子包下的所有类,范围过大会影响启动速度。应尽量缩小扫描范围:

// 不好的做法:扫描范围过大 @ComponentScan(basePackages ="com.example") // 好的做法:精确到具体子包 @ComponentScan(basePackages ={ "com.example.demo.service", "com.example.demo.repository" })

6.2 使用懒加载

对于非核心 Bean,可使用@Lazy注解延迟初始化,避免在容器启动时实例化:

@Service @Lazy// 懒加载:第一次使用时才实例化 public class ReportService{ // ... }

6.3 减少 Bean 数量

  • 合并功能相似的 Bean
  • 移除不必要的 Bean
  • 使用@Conditional根据环境条件注册 Bean

@Service @Conditional(ProductionCondition.class)// 仅在生产环境注册 publicclassProductionMonitoringService{ // ... }

6.4 优化 Bean 初始化逻辑

  • 将耗时操作从 Bean 的初始化方法(@PostConstruct)中移走
  • 对于需要初始化大量数据的 Bean,考虑使用异步初始化

@Service @Slf4j public class DataInitializer{ @Async// 异步初始化 @PostConstruct public void initData(){ log.info("开始异步初始化数据..."); // 耗时的数据初始化操作 } }

6.5 使用 Spring Boot 的 devtools

在开发环境中,Spring Boot 的 devtools 可以实现快速重启,大大缩短启动时间:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>

七、常见问题与解决方案

7.1 容器启动失败:Bean 定义冲突

问题NoUniqueBeanDefinitionException,提示存在多个同类型的 Bean。

解决方案

  • 使用@Primary指定首选 Bean
  • 使用@Qualifier指定具体 Bean 名称
  • 检查是否有重复的 Bean 定义(如同一类被多次扫描)
代码语言:javascript
复制
@Service
@Primary // 指定为首选Bean
public class PrimaryUserService implements UserService { ... }

@Service
public class SecondaryUserService implements UserService { ... }

// 使用时
@Autowired
private UserService userService; // 会注入PrimaryUserService

7.2 容器启动缓慢:扫描路径过大

问题:容器启动时间过长,主要花费在组件扫描阶段。

解决方案

缩小@ComponentScan的扫描范围

排除不需要扫描的包或类

代码语言:javascript
复制
@ComponentScan(
    basePackages = "com.example.demo",
    excludeFilters = {
        @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.example.demo.test.*"),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = TestService.class)
    }
)
代码语言:javascript
复制

7.3 依赖注入失败:Bean 未被扫描

问题NoSuchBeanDefinitionException,提示找不到指定的 Bean。

解决方案

  • 检查类是否标注了@Component@Service等注解
  • 检查类是否在@ComponentScan的扫描范围内
  • 检查是否有拼写错误(Bean 名称或类名)
代码语言:javascript
复制
// 常见错误:忘记添加@Component注解
public class MissingAnnotationService { ... }

// 正确做法:添加@Component或其派生注解
@Service
public class CorrectService { ... }

7.4 循环依赖问题

问题BeanCurrentlyInCreationException,提示 Bean 正在创建中,出现循环依赖。

解决方案

  • 使用字段注入或 setter 注入(Spring 默认支持解决单例 Bean 的循环依赖)
  • 避免构造器注入的循环依赖
  • 使用@Lazy注解延迟初始化其中一个依赖
代码语言:javascript
复制
@Service
public class AService {
    private final BService bService;

    // 使用@Lazy解决构造器循环依赖
    public AService(@Lazy BService bService) {
        this.bService = bService;
    }
}

@Service
public class BService {
    private final AService aService;

    public BService(AService aService) {
        this.aService = aService;
    }
}

八、总结:掌握容器启动流程的价值

Spring 容器的启动流程是 Spring 框架的核心知识,掌握这一流程对开发者有多重价值:

  1. 问题排查能力:当容器启动失败或出现奇怪的 Bean 注入问题时,能够根据启动流程定位根源。
  2. 性能优化能力:了解启动流程中的性能瓶颈(如组件扫描、Bean 实例化),有针对性地进行优化。
  3. 框架扩展能力:基于对BeanFactoryPostProcessorBeanPostProcessor等扩展点的理解,能够开发出更强大的自定义组件。
  4. 深度理解 Spring 特性:许多 Spring 特性(如 AOP、事务管理)的实现都依赖于容器启动过程中的特定步骤,理解这些步骤有助于更好地使用这些特性。

Spring 容器的启动流程虽然复杂,但并非无章可循。通过本文介绍的 11 个核心步骤,我们可以系统地理解容器从初始化到最终就绪的全过程。无论是refresh()方法的整体框架,还是 Bean 实例化的细节流程,每一部分都体现了 Spring 框架的设计思想。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-08-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 果酱带你啃java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、引言:Spring 容器 ——Java 应用的 "神经中枢"
  • 二、Spring 容器的基石:核心接口与实现类
    • 2.1 核心接口层次
    • 2.2 常用实现类
  • 三、Spring 容器启动的 10 大核心步骤
    • 3.1 步骤 1:初始化容器实例
    • 3.2 步骤 2:设置配置源
    • 3.3 步骤 3:刷新容器 —— 启动的核心入口
    • 3.4 步骤 4:准备刷新 —— 初始化环境与验证
    • 3.5 步骤 5:获取 BeanFactory—— 创建或刷新 Bean 工厂
    • 3.6 步骤 6:准备 BeanFactory—— 配置 Bean 工厂的标准组件
    • 3.7 步骤 7:执行 BeanFactory 后置处理器 —— 扩展 Bean 定义
    • 3.8 步骤 8:注册 Bean 后置处理器 —— 增强 Bean 实例化过程
    • 3.9 步骤 9:初始化消息源和事件机制
      • 9.1 初始化消息源(initMessageSource ())
      • 9.2 初始化事件多播器(initApplicationEventMulticaster ())
    • 3.10 步骤 10:实例化非懒加载单例 Bean
    • 3.11 步骤 11:完成刷新 —— 发布容器就绪事件
  • 四、Spring 容器启动流程全景图
  • 五、实战分析:追踪容器启动过程
  • 六、容器启动的性能优化策略
    • 6.1 减少扫描范围
    • 6.2 使用懒加载
    • 6.3 减少 Bean 数量
    • 6.4 优化 Bean 初始化逻辑
    • 6.5 使用 Spring Boot 的 devtools
  • 七、常见问题与解决方案
    • 7.1 容器启动失败:Bean 定义冲突
    • 7.2 容器启动缓慢:扫描路径过大
    • 7.3 依赖注入失败:Bean 未被扫描
    • 7.4 循环依赖问题
  • 八、总结:掌握容器启动流程的价值
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档