IOC - refresh

Spring IOC 容器启动:refresh() 十二步

refresh() 解决的问题:把一个刚 new 出来的空壳 ApplicationContext 变成完全可用的 IOC 容器。
XML、注解、Spring Boot 的启动入口殊途同归,最终都汇到这个模板方法,十二步顺序写死,子类和扩展点只能往固定的槽位里塞内容,改不了流程本身。
本地快照是 v5.3.39;6.1 起入口锁从 synchronized 换成了 startupShutdownLock(ReentrantLock,避免 synchronized 钉住虚拟线程),但十二步骨架十多年没变过。

代码块JAVA · 70 行收起展开
// 基于本地 Spring 仓 (v5.3.39), org.springframework.context.support.AbstractApplicationContext#refresh
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {    // 和 close()/doClose() 共用同一把监视器锁: 启动与销毁绝不并发
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");   // 启动耗时埋点, 默认实现是空操作

        // Prepare this context for refreshing.
        prepareRefresh();   // 1. 置 active 标志、校验必需属性、准备早期事件暂存集合

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();     // 2. 拿到 DefaultListableBeanFactory; 注解驱动下 BeanDefinition 大多还没进来, 留给第 5 步扫描

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);    // 3. 给工厂做标配: Aware 回调、可解析依赖、environment 等内置单例

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);    // 4. 子类钩子: Web 容器在此注册 request/session 作用域

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);   // 5. 执行 BeanFactoryPostProcessor: 配置类解析、包扫描都在这, 把"定义"补齐

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);    // 6. 只注册不调用: AOP 代理创建器等在此排好序就位, 等第 11 步创建 bean 时才回调
            beanPostProcess.end();

            // Initialize message source for this context.
            initMessageSource();    // 7. 国际化; 没配就注册一个空实现兜底

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();  // 8. 事件广播器, publishEvent 的底层设施

            // Initialize other special beans in specific context subclasses.
            onRefresh();    // 9. 子类钩子: Boot 内嵌 Tomcat 的 createWebServer 就在这里

            // Check for listener beans and register them.
            registerListeners();    // 10. 挂监听器 + 补发早期事件

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);   // 11. 实例化全部非懒加载单例: 依赖注入、AOP 织入、循环依赖处理都发生在这一步内部

            // Last step: publish corresponding event.
            finishRefresh();    // 12. 启动 Lifecycle 组件, 发布 ContextRefreshedEvent
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();     // 启动失败销毁已建单例: 半成品容器不能泄漏连接池、线程这类资源

            // Reset 'active' flag.
            cancelRefresh(ex);  // active 置回 false, 之后再 getBean 直接抛 IllegalStateException

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();    // 成败都清反射/注解元数据缓存: 这些缓存只为启动期装配服务, 单例建完就是死重
            contextRefresh.end();
        }
    }
}

前三步是准备阶段,重点看两处:earlyApplicationEvents 的暂存设计,和 prepareBeanFactory 里 ignore 与 register 成对出现的依赖规则。

代码块JAVA · 67 行收起展开
// 基于本地 Spring 仓 (v5.3.39), AbstractApplicationContext 准备阶段三个方法
protected void prepareRefresh() {
    // Switch to active.
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);     // closed/active 是两个 AtomicBoolean: close() 过的容器理论上还能再 refresh 复活
    this.active.set(true);

    // ... 日志输出省略

    // Initialize any placeholder property sources in the context environment.
    initPropertySources();      // 子类钩子: Web 环境把 servletContextInitParams 这类占位 PropertySource 换成真实例

    // Validate that all properties marked as required are resolvable:
    // see ConfigurablePropertyResolver#setRequiredProperties
    getEnvironment().validateRequiredProperties();  // 必需属性缺失在启动第一步就快速失败, 不拖到运行时

    // Store pre-refresh ApplicationListeners...
    if (this.earlyApplicationListeners == null) {
        this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    }
    else {
        // Reset local application listeners to pre-refresh state.
        this.applicationListeners.clear();
        this.applicationListeners.addAll(this.earlyApplicationListeners);   // 支持重复 refresh: 把监听器列表还原到首次 refresh 前的快照
    }

    // Allow for the collection of early ApplicationEvents,
    // to be published once the multicaster is available...
    this.earlyApplicationEvents = new LinkedHashSet<>();    // 广播器第 8 步才建, 此前发的事件先攒在这, 第 10 步统一补发
}

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();   // 子类实现: XML 系容器销毁旧工厂重建并 loadBeanDefinitions; GenericApplicationContext 只做一次性校验, 工厂构造时就建好了
    return getBeanFactory();
}

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // Tell the internal bean factory to use the context's class loader etc.
    beanFactory.setBeanClassLoader(getClassLoader());
    if (!shouldIgnoreSpel) {
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));    // @Value("#{...}") 的 SpEL 解析能力从这来
    }
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // Configure the bean factory with context callbacks.
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));   // 各种 Aware 接口的注入靠这个 BeanPostProcessor 在初始化前回调
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);  // 成对设计: Aware 的 setter 不参与按类型自动装配, 注入职责已归上面的 Processor, 防二次注入
    // ... 其余 6 个 ignoreDependencyInterface(XxxAware.class) 同理省略

    // BeanFactory interface not registered as resolvable type in a plain factory.
    // MessageSource registered (and found for autowiring) as a bean.
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);   // 反向操作: 这四个类型没有 BeanDefinition, 但 @Autowired 必须能注入
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);   // 注入 ApplicationContext 拿到的就是容器自身 this

    // Register early post-processor for detecting inner beans as ApplicationListeners.
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));    // 实现 ApplicationListener 的 bean 初始化完自动挂上广播器

    // ... LoadTimeWeaver 检测省略

    // Register default environment beans.
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); // 以手工单例注册, 不走 bean 创建流程
    }
    // ... systemProperties / systemEnvironment / applicationStartup 三个同理省略
}

真正要记死的是 5、6、11 三步。第 5 步把”定义”补齐,第 6 步把”实例加工器”排好队,第 11 步才统一实例化。

代码块JAVA · 76 行收起展开
// 基于本地 Spring 仓 (v5.3.39), AbstractApplicationContext 第 5/6/10/11/12 步实现
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());     // 真正干活的委托类: 先 BeanDefinitionRegistryPostProcessor 后普通 BFPP, 各自按 PriorityOrdered > Ordered > 无序执行
    // ... LoadTimeWeaver 兜底检测省略
}

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);    // 委托类里会 getBean 把所有 BPP 提前实例化并排序注册, 但一个都不回调
}

protected void registerListeners() {
    // Register statically specified listeners first.
    for (ApplicationListener<?> listener : getApplicationListeners()) {
        getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let post-processors apply to them!
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);   // 只按类型查名字不触发实例化: 此刻 getBean 会让监听器漏掉 BPP 加工
    for (String listenerBeanName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);  // 登记的是 beanName, 实例化推迟到第 11 步
    }

    // Publish early application events now that we finally have a multicaster...
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;     // 置 null 同时是个开关: 此后 publishEvent 直接走广播器, 不再进暂存集合
    if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);    // 把 prepareRefresh 起攒下的早期事件补发出去
        }
    }
}

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // Initialize conversion service for this context.
    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));
    }

    // Register a default embedded value resolver if no BeanFactoryPostProcessor
    // (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
    // at this point, primarily for resolution in annotation attribute values.
    if (!beanFactory.hasEmbeddedValueResolver()) {
        beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));   // ${} 占位符解析的兜底实现
    }

    // ... LoadTimeWeaverAware 与临时 ClassLoader 处理省略

    // Allow for caching all bean definition metadata, not expecting further changes.
    beanFactory.freezeConfiguration();  // 冻结定义: 从此可以放心缓存 merged BeanDefinition, 元数据不许再改

    // Instantiate all remaining (non-lazy-init) singletons.
    beanFactory.preInstantiateSingletons();     // 十二步里最重的一行: 遍历全部 beanDefinitionNames 逐个 getBean
}

@SuppressWarnings("deprecation")
protected void finishRefresh() {
    // Clear context-level resource caches (such as ASM metadata from scanning).
    clearResourceCaches();

    // Initialize lifecycle processor for this context.
    initLifecycleProcessor();

    // Propagate refresh to lifecycle processor first.
    getLifecycleProcessor().onRefresh();    // 启动所有 autoStartup 的 SmartLifecycle: 消息监听容器这类"活"组件从这里跑起来

    // Publish the final event.
    publishEvent(new ContextRefreshedEvent(this));  // 容器就绪的官方信号, 启动后初始化逻辑监听它最稳

    // Participate in LiveBeansView MBean, if active.
    if (!NativeDetector.inNativeImage()) {
        LiveBeansView.registerApplicationContext(this);     // 5.3 已废弃, 6.0 连同这段代码一起删除
    }
}

原理串讲

new AnnotationConfigApplicationContext(AppConfig.class) 走一遍。
构造器先 register(AppConfig),此刻 DefaultListableBeanFactory 里只有 AppConfig 自己和几个内置处理器的 BeanDefinition(最关键的是 ConfigurationClassPostProcessor),然后进 refresh()
prepareRefresh() 里最值得看的是 earlyApplicationEvents = new LinkedHashSet<>() 这行:事件广播器要到第 8 步才创建,但 publishEvent 从容器一出生就可能被调用,为什么不直接报错或丢弃?Spring 选择先攒后补,publishEvent 内部判断暂存集合非 null 就只入集合,registerListeners() 末尾把集合置 null 并把攒下的事件逐个 multicastEvent 补发。
用一个字段同时当缓冲区和开关,事件一条不丢,也不需要额外的状态机。第 2 步 obtainFreshBeanFactory() 在注解容器里几乎是空操作(GenericApplicationContext.refreshBeanFactory 只 CAS 校验不许二次 refresh),XML 系的 AbstractRefreshableApplicationContext 则在这里销毁旧工厂、重建、loadBeanDefinitions 解析 XML,这也是”XML 容器定义在第 2 步齐、注解容器定义在第 5 步齐”的分岔点。

代码块JAVA · 2 行收起展开
5 步 `invokeBeanFactoryPostProcessors` 委托给 `PostProcessorRegistrationDelegate`:先执行所有 `BeanDefinitionRegistryPostProcessor`,其中 `ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry` 解析 `@Configuration`、`@ComponentScan`、`@Bean`、`@Import`,递归扫描把成百上千个 BeanDefinition 注册进工厂;再执行普通 `BeanFactoryPostProcessor`,比如同一个类的 `postProcessBeanFactory` 会给 `@Configuration` 类做 CGLIB 增强,保证配置类里 `@Bean` 方法互调时拿到的是容器单例而非新对象。
为什么把"改定义""实例化"切成两个阶段?因为 BFPP 面对的是元数据,此时改 scope、改属性值、加减定义都还来得及;一旦开始实例化,定义就被 merge 并缓存,再改也不生效。

阶段切开后每个扩展点的语义边界非常清楚:BFPP 改”图纸”,BPP 改”成品”。

第 6 步 registerBeanPostProcessors 把所有 BPP 提前 getBean 出来、按序 addBeanPostProcessor,但一个也不回调。
为什么只注册不调用?因为 BPP 的回调点位长在每个 bean 的创建流程内部(初始化前后各一次),它必须赶在任何业务 bean 实例化之前全部就位,否则先创建的 bean 会漏掉 AOP 织入这类加工;谁要是被过早实例化,日志里 BeanPostProcessorChecker 就会告警 “is not eligible for getting processed by all BeanPostProcessors”。
这也是第 10 步 registerListeners 只登记 beanName 不实例化监听器的原因:监听器也是 bean,也要完整走一遍加工线。
第 11 步 finishBeanFactoryInitializationfreezeConfiguration() 冻结定义,然后 preInstantiateSingletons() 遍历 beanDefinitionNames 逐个 getBean → doGetBean → createBean → doCreateBean:实例化、populateBean 属性注入、initializeBean 初始化,AOP 代理在 postProcessAfterInitialization 里把原对象换掉,循环依赖靠三级缓存提前暴露半成品(细节见 循环依赖-三级缓存AOP-动态代理)。
全部单例建完后还会回调 SmartInitializingSingleton.afterSingletonsInstantiated,@EventListener 注解方法就是这时被解析成监听器挂上广播器的。

第 12 步 finishRefreshgetLifecycleProcessor().onRefresh() 启动所有 autoStartup 的 SmartLifecycle,最后 publishEvent(new ContextRefreshedEvent(this)) 广播容器就绪。
失败路径同样有设计:catch 里 destroyBeans() 逐个执行已建单例的销毁回调,防止半成品容器泄漏连接池和线程;cancelRefresh 把 active 置 false,此后 getBean 直接抛 IllegalStateException,杜绝一个启动失败的容器被继续使用。
finally 里 resetCommonCaches() 成败都执行,把反射、注解、ResolvableType 的元数据缓存清空,这批缓存只在装配期有用,单例建完留着就是白占内存。

设计取舍

  • 模板方法把顺序焊死,扩展全走钩子(postProcessBeanFactory/onRefresh)和处理器(BFPP/BPP):子类只能改内容改不了流程,这是 refresh 骨架十几年不腐的原因。
  • BFPP 改定义、BPP 改实例,别混用:在 BFPP 里 getBean 是经典误用,会把 bean 提前实例化、绕开尚未注册的后置处理器。
  • 事件系统对启动时序免疫:第 8 步前发的事件进 earlyApplicationEvents 暂存补发,监听器 bean 第 11 步才实例化但第 10 步已按名登记,两头都不丢。
  • 单例默认非懒加载是刻意的:启动慢一点,换来配置错误在启动期全部暴露,运行时 getBean 只剩查缓存。
  • 5.3.x 用 synchronized 锁整个 refresh;6.1 起换成 startupShutdownLock(ReentrantLock),主因是虚拟线程时代 synchronized 长临界区会钉住载体线程。

延伸阅读