六)Spring Boot 源码研读之prepareContext 准备上下文

源码

先看下prepareContext源码,然后让我们逐步分析一下在该阶段 Spring Boot 做了哪些操作。

java 复制代码
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
       ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
       ApplicationArguments applicationArguments, Banner printedBanner) {
    //设置环境
    context.setEnvironment(environment);
    //后置处理
    postProcessApplicationContext(context);
    //按需添加aot初始化器
    addAotGeneratedInitializerIfNecessary(this.initializers);
    //执行所有的 ApplicationContextInitializer
    applyInitializers(context);
    //执行应用上下文准备完成事件
    listeners.contextPrepared(context);
    //执行启动上下文关闭事件
    bootstrapContext.close(context);
    //打印启动日志和profile
    if (this.logStartupInfo) {
       logStartupInfo(context.getParent() == null);
       logStartupProfileInfo(context);
    }
    // 注册相关的单例bean
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
       beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    // 设置beanFactory 不允许循环引用 和 不允许bean定义覆盖
    if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
       autowireCapableBeanFactory.setAllowCircularReferences(this.allowCircularReferences);
       if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
          listableBeanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
       }
    }
    if (this.lazyInitialization) {
       context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    if (this.keepAlive) {
       context.addApplicationListener(new KeepAlive());
    }
    context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
    // 注册bean定义,load内部
    if (!AotDetector.useGeneratedArtifacts()) {
       // Load the sources
       Set<Object> sources = getAllSources();
       Assert.notEmpty(sources, "Sources must not be empty");
       load(context, sources.toArray(new Object[0]));
    }
    // 执行应用上下文加载完成事件
    listeners.contextLoaded(context);
}

设置环境

将 ApplicationServletEnvironment 实例对象设置到应用上下文中

postProcessApplicationContext

应用上下文的后置处理,用于在应用上下文创建之后做一些额外的处理

java 复制代码
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    if (this.beanNameGenerator != null) {
       context.getBeanFactory()
          .registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
       if (context instanceof GenericApplicationContext genericApplicationContext) {
          genericApplicationContext.setResourceLoader(this.resourceLoader);
       }
       if (context instanceof DefaultResourceLoader defaultResourceLoader) {
          defaultResourceLoader.setClassLoader(this.resourceLoader.getClassLoader());
       }
    }
    if (this.addConversionService) {
       context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService());
    }
}

从源码看主要做了以下几件事:

  • 注册自定义的bean命名生成器

  • 注入自定义的 ResourceLoader,并共用 ResourceLoader 相应的 ClassLoader

  • 复用环境中的转换服务,共用一套,避免重复构造

添加 aot 初始化器

这个主要作用是如果启用了aot,则生成对应的aot Initializer,然后将这个aot Initializer 放到 initializers 列表的最前面,优先起作用。aot 是spring应用在构建期预运行一次main,并对一些反射bean的解析提前算好生成对应的Initializer,Initializer里包含了算好的BeanDefinition。这样在触发 initializer 时,就能提前完成bean的注入,而不需要等到执行期的扫描阶段再通过反射方式去完成bean的注入(反射注入bean相对比较耗时),这样启动就很快。

执行所有的 ApplicationContextInitializer

java 复制代码
protected void applyInitializers(ConfigurableApplicationContext context) {
    for (ApplicationContextInitializer initializer : getInitializers()) {
       Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
             ApplicationContextInitializer.class);
       Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
       initializer.initialize(context);
    }
}

执行应用上下文准备完成事件

事件类型:ApplicationContextInitializedEvent

java 复制代码
listeners.contextPrepared(context);


//类:EventPublishingRunListener
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
    multicastInitialEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}

执行启动上下文的close事件

java 复制代码
bootstrapContext.close(context);

打印启动日志和profile

java 复制代码
if (this.logStartupInfo) {
    logStartupInfo(context.getParent() == null);
    logStartupProfileInfo(context);
}

注入相关bean、beanFactory定义bean方式设置、应用上下文设置

  • 注册 springApplicationArguments 单例bean

  • 注册 springBootBanner 单例bean

  • 设置是否允许循环引用 allowCircularReferences,默认false

  • 设置是否允许bean定义覆盖 allowBeanDefinitionOverriding,默认false

  • 如果支持延迟初始化,则设置 LazyInitializationBeanFactoryPostProcessor

  • 如果要保活(无存活的非守护线程时,也保持应用存活),设置KeepAlive ApplicationListener

  • 添加 PropertySourceOrderingBeanFactoryPostProcessor,用于重排序,将 defaultProperties 属性资源PropertySource 移到最后,优先级最低

注册bean定义

在非aot模式下,完成Sources关联的bean定义的加载,经过这一步 BeanFactory 中就有了'未实例化'的BeanDefinition 集合。关于如何加载可见《Spring Boot 之 BeanDefinitionLoader 详解-CSDN博客》

java 复制代码
if (!AotDetector.useGeneratedArtifacts()) {
    // Load the sources
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    load(context, sources.toArray(new Object[0]));
}

执行应用上下文上下文加载完成事件

事件类型:ApplicationPreparedEvent

java 复制代码
listeners.contextLoaded(context);


//类:EventPublishingRunListener
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
    for (ApplicationListener<?> listener : this.application.getListeners()) {
       if (listener instanceof ApplicationContextAware contextAware) {
          contextAware.setApplicationContext(context);
       }
       context.addApplicationListener(listener);
    }
    multicastInitialEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}

总结

通过以上分析,我们得到prepareContext 执行流程如下图所示

注:spring boot 版本为3.2.3

相关推荐
古法老登瞎折腾6 小时前
大数据智能运维实战(十一):运维 MCP 的工程化——权限、限流、审计与错误处理
java·大数据·运维·spring ai·mcp
智能运维指南6 小时前
研制过程“两张皮“怎么破:研制一体化支撑平台的定位、能力边界与落地前提
java·前端·人工智能·嘉为蓝鲸
小满zs6 小时前
Go语言第十七章(反射)
后端·go
小满zs6 小时前
Go语言第十五章(JSON序列化)
后端·go
小满zs6 小时前
Go语言第十六章(自定义类型)
后端·go
yz_aiks7 小时前
Spring AI 2.1 ChatMemory 已过时用什么替代:迁移到 Session Memory 实战(JDBC 存储与会话压缩)
java·spring boot·大模型·ai agent·spring ai
金銀銅鐵7 小时前
[Java] 用 GUI 展示 EnumMap 增删键值对时的内部变化
java·后端·ai编程
学长毕设7 小时前
基于SpringBoot的上门洗车服务系统设计与实现
java·spring boot·后端
jason成都7 小时前
RTKLIB-Java 多基线联合RTK:FIX率从0%到91.5%的三层根因修复
java·gnss·rtklib·rtklib_java
鱼宵8 小时前
Stream 终端操作一次讲透:collect、count、reduce 怎么选
java·stream·终端操作