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 :)

相关推荐
g323086319 分钟前
springboot封装请求参数json的源码解析
spring boot·后端·json
赫萝的红苹果20 分钟前
基于Redisson实现分布式锁
java·spring boot·分布式
=(^.^)=哈哈哈36 分钟前
Go语言实现的端口扫描工具示例
开发语言·后端·golang
孤寒者1 小时前
(三十一)Flask之wtforms库【剖析源码下篇】
后端·python·flask·源码剖析·wtforms
milong5211 小时前
Flask自定义命令
后端·python·flask
knighthood20011 小时前
flask中解决图片不显示的问题(很细微的点)
后端·python·flask
高级程序源2 小时前
springboot学生档案信息管理系统-计算机毕业设计源码96509
java·spring boot·spring·eclipse·mybatis·idea
努力的蚂蚁【你若】2 小时前
Vue打包文件dist放在SpringBoot项目下运行(正确实现全过程)
前端·vue.js·spring boot
小子爱吃鱼2 小时前
ERP的模块说明
后端·工厂方法模式
不要飞升3 小时前
百日筑基第十一天-看看SpringBoot
java·spring boot·后端·实习