Spring Boot源码分九:ApplicationContext属性赋值

前言

本文是作者写关于Spring源码的第一篇文章,作者水平有限,所有的源码文章仅限用作个人学习记录。文中如有错误欢迎各位留言指正。

上一篇文章分析到Spring Boot启动的时候初始化了ApplicationContext容器。今天接着看容器初始化了后,对容器对象做了哪些操作。

设置ApplicationsStartUp对象

java 复制代码
public ConfigurableApplicationContext run(String... args) {
// 为了计时用的,老版本和新版本不一样
   long startTime = System.nanoTime();
   // 初始化一个引导器的上下文,这是属于Spring Boot的上下文。后边还有一个Spring的上下文。apach好喜欢context这个东西,证明写框架这个context是真的好用。
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   // 这是Spring的上下文,在这里定义,在下面进行的初始化
   ConfigurableApplicationContext context = null;
   // 配置一个系统属性
   configureHeadlessProperty();
   // 获取配置文件的监听器 重点 也是扩展点,凡是读取配置文件的地方都是扩展点,因为配置在配置文件中的initializer、listener都会在某个阶段被调用
   SpringApplicationRunListeners listeners = getRunListeners(args);
   // 调用监听器发送启动事件,这里可以通过自定义监听器消费这个事件,处理自己的逻辑
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
   // 解析命令行参数 将其封装成一个ApplicationArguments,这个类的变量name被设置成commandLineArgs字符串,变量source是解析args封装的CommandLineArgs对象。
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 环境预处理
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      // 配置忽略beanInfo
      configureIgnoreBeanInfo(environment);
      // 打印banner信息
      Banner printedBanner = printBanner(environment);
      // 创建ApplicationContext容器
      context = createApplicationContext();
      // 给上下文设置应用启动对象
      context.setApplicationStartup(this.applicationStartup);
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
      }
      listeners.started(context, timeTakenToStartup);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }
   try {
      Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
      listeners.ready(context, timeTakenToReady);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

setApplicationStartup

applicationStartup是一个应用启动对象。该函数的作用是将应用启动对象与上下文对象关联起来,以便在上下文对象中可以访问应用启动对象的相关属性和方法。

prepareContext

传入的参数绑定到特定的上下文对象中,为应用程序的启动和运行做好准备。

主要做了以下操作:

  • 将bootstrapContext绑定到context中,以便在应用程序中访问引导上下文信息。

  • 将environment绑定到context中,以便在应用程序中访问环境变量和属性。

  • 将listeners绑定到context中,以便在应用程序启动和停止时触发相应的监听器事件。

  • 将applicationArguments绑定到context中,以便在应用程序中访问命令行参数。

  • 将printedBanner绑定到context中,以便在应用程序启动时控制是否打印欢迎信息。

通过执行这些操作,prepareContext函数为应用程序的上下文环境准备了必要的信息和配置,使得应用程序能够正常启动和运行。

我们进入到方法看一下。

java 复制代码
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
       ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
       ApplicationArguments applicationArguments, Banner printedBanner) {
    // 将当前的环境变量赋值给context的environment属性,所有在后续的操作中,可以通过context对象获取到当前的环境变量信息
    context.setEnvironment(environment);
    // 给容器中添加一些bean信息
    postProcessApplicationContext(context);
    applyInitializers(context);
    listeners.contextPrepared(context);
    bootstrapContext.close(context);
    if (this.logStartupInfo) {
       logStartupInfo(context.getParent() == null);
       logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
       beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
       ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
       if (beanFactory instanceof DefaultListableBeanFactory) {
          ((DefaultListableBeanFactory) beanFactory)
             .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
       }
    }
    if (this.lazyInitialization) {
       context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
    // 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);
}

postProcessApplicationContext

主要用于在Spring应用程序上下文初始化后,根据配置进行一些额外的设置和配置。

  • 如果设置了beanNameGenerator,则将其注册为一个单例bean,用于生成bean的名称。

  • 如果设置了resourceLoader,则根据上下文类型的不同,分别设置上下文的资源加载器或类加载器。

  • 如果addConversionService为true,则将环境的转换服务设置为bean工厂的转换服务。

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) context).setResourceLoader(this.resourceLoader);
       }
       if (context instanceof DefaultResourceLoader) {
          ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
       }
    }
    if (this.addConversionService) {
       context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService());
    }
}

applyInitializers

依次调用获取到的ApplicationContextInitializer类型的实例来初始化ConfigurableApplicationContext上下文。具体实现是通过for-each循环遍历getInitializers()方法返回的initializer数组,然后通过GenericTypeResolver.resolveTypeArgument()方法获取initializer的实际类型,再使用Assert.isInstanceOf()方法判断context是否是该类型,最后调用initializer.initialize(context)方法来初始化上下文。

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);
    }
}

OK 今天先到这里吧。

See you next time :)

相关推荐
wyiyiyi16 分钟前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
一只爱撸猫的程序猿1 小时前
使用Spring AI配合MCP(Model Context Protocol)构建一个"智能代码审查助手"
spring boot·aigc·ai编程
甄超锋1 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
阿华的代码王国1 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy1 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack2 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9653 小时前
pip install 已经不再安全
后端
寻月隐君3 小时前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github
武昌库里写JAVA3 小时前
JAVA面试汇总(四)JVM(一)
java·vue.js·spring boot·sql·学习
Pitayafruit4 小时前
Spring AI 进阶之路03:集成RAG构建高效知识库
spring boot·后端·llm