Spring WebFlux 工作原理分析 - 2.应用启动过程--2.创建应用上下文

本文深入分析SpringBoot中Reactive Web应用上下文的创建与初始化过程,包括环境配置、上下文对象创建及准备阶段。关键步骤涉及prepareEnvironment、createApplicationContext和prepareContext,特别关注Reactive Web环境的特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在该系列上一篇文章中,我们分析了应用根据classpath存在的Web环境的特征类的存在性,判断出当前Web环境是WebApplicationType.REACTIVE。这一篇文章,我们主要来分析应用程序上下文ApplicationContext的创建,初始化和准备过程。这一过程,主要体现在类SpringApplication实例成员方法ConfigurableApplicationContext run(String... args):

// 类 SpringApplication 代码片段
	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
          // 包装通过命令行传入的名命令行参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
          // 结合命令行参数 准备环境对象,该环境对象将会被设置到应用上下文对象 ApplicationContext 上  ,
          // 环境对象通常包含如下信息 : 
          // 1. profile
          // 2. system properties
          // 3. system environment
          // 4. commandline arguments
          // 5. spring 配置文件
          // 6. 一个随机值属性源 random
          // 对于当前 WebFlux 应用来讲,这里实现类会使用 StandardReactiveWebEnvironment
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
          // 创建应用上下文对象 ApplicationContext  
          // 实现类会采用 : AnnotationConfigReactiveWebServerApplicationContext
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
          // 准备应用上下文对象 ApplicationContext          
          // 1. 关联环境信息对象到应用上下文对象
          // 2. 对象创建后置处理 : 设置容器的类型转换服务
          // 3. 初始化应用上下文对象:调用各个 ApplicationContextInitializer
          // 4. 广播事件 : ApplicationContextInitializedEvent
          // 5. 将应用程序参数作为一个 bean 注册到容器 : springApplicationArguments
          // 6. 将应用程序入口类作为 bean 注册到容器 (load)
          // 7. 上下文加载完成生命周期事件回调,为各个实现了 接口 ApplicationContextAware 的 
          //    ApplicationListener 设置应用上下文对象属性, 并广播事件 : ApplicationPreparedEvent
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
          // 刷新应用上下文对象  ApplicationContext 
          // 主要是调用应用上下文对象  ApplicationContext  自身的 refresh 方法
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
            
          // 应用程序上下文对象 ApplicationContext 已经准备就绪,
          // 现在调用各种开发人员或者框架其他部分定义的 
          // ApplicationRunner 或者 CommandLineRunner
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

SpringApplication#run的逻辑其实是应用启动的整个过程,其中,能体现应用启动的关键步骤主要是 :

  1. prepareEnvironment
  2. createApplicationContext
  3. prepareContext
  4. refreshContext

我们将逐一分析 。这篇文章中,我们主要分析 :

  1. prepareEnvironment
  2. createApplicationContext
  3. prepareContext

这三个步骤覆盖了应用上下文对象创建前的准备工作:创建环境信息对象,创建应用上下文对象本身,以及准备应用上下文。当应用上下文准备好之后,必须要调用第四个步骤refreshContext刷新应用上下文,才能算是应用完全启动。不过对于refreshContext主要是调用应用上下文对象的refresh方法,我们将其放到下一篇重点分析。

1. prepareEnvironment

// SpringApplication 代码片段
	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
       // 创建环境信息对象 environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
       // 将应用程序参数关联到环境信息对象 environment
		configureEnvironment(environment, applicationArguments.getSourceArgs());
       // 发布应用程序事件 : 环境信息对象准备好了 ,
       // 同步调用各个事件监听器
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}       
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

以上代码针对各种Web环境通用,所以这里看不出针对Reactive Web环境有什么特殊的地方。我们需要深一步观察:

// SpringApplication 代码片段
    private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}

这段代码是用来创建环境信息对象的,从其逻辑不难看出,它根据属性this.webApplicationType决定环境信息对象使用哪个实现类,从上一篇的分析我们已经可以知道,this.webApplicationTypeREACTIVE,所以,这里的环境信息对象实现类会使用StandardReactiveWebEnvironment

2. createApplicationContext

准备好环境对象之后,SpringApplication要创建应用上下文对象了,该动作实现在方法createApplicationContext中:

// SpringApplication 代码片段
	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
             // 根据 this.webApplicationType 确定应用上下文实现类
				switch (this.webApplicationType) {
				case SERVLET:
            // DEFAULT_SERVLET_WEB_CONTEXT_CLASS 常量值为 : 
            // org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
            // 对应 Spring MVC Servlet Web 环境
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
            // DEFAULT_REACTIVE_WEB_CONTEXT_CLASS 常量值为 : 
            // org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext
            // 对应 Spring WebFlux Reactive Web 环境
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
            // DEFAULT_CONTEXT_CLASS 常量值为 : 
            // org.springframework.context.annotation.AnnotationConfigApplicationContext
            // 不对应任何 Web 环境
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
			"Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
			ex);
			}
		}
        
       // 确定应用上下文实现类之后,实例化应用上下文对象 
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

getOrCreateEnvironment方法类似,createApplicationContext也根据this.webApplicationType决定应用上下文对象使用哪个实现类。在本系列文章所用的例子中,显然这里是org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext

3.prepareContext

// SpringApplication 代码片段
	private void prepareContext(ConfigurableApplicationContext context, 
			ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, 
			ApplicationArguments applicationArguments, 
			Banner printedBanner) {
       // 1. 关联环境信息对象到应用上下文对象     
		context.setEnvironment(environment);
       // 2. 对象创建后置处理 : 设置容器的类型转换服务 
		postProcessApplicationContext(context);
       // 3. 初始化应用上下文对象:调用各个 ApplicationContextInitializer 
		applyInitializers(context);
       // 4. 广播事件 : ApplicationContextInitializedEvent 
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
       // 5. 将应用程序参数作为一个 bean 注册到容器 : springApplicationArguments 
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
       // 6. 将应用程序入口类作为 bean 注册到容器 (load) 
		load(context, sources.toArray(new Object[0]));
       // 7. 上下文加载完成生命周期事件回调,为各个实现了 接口 ApplicationContextAware 的 
       //    ApplicationListener 设置应用上下文对象属性, 并广播事件 : ApplicationPreparedEvent        
		listeners.contextLoaded(context);
	}

从方法prepareContext实现来看,它针对不同实现类的ApplicationContext的处理流程并无不同,换句话讲,以上流程无论是针对Servlet Web环境的AnnotationConfigServletWebServerApplicationContext,还是WebFlux Reactive Web环境的AnnotationConfigReactiveWebServerApplicationContext,所做的事情都一样。

总结

本文分析了应用上下文对象创建到准备的以下步骤 :

  1. prepareEnvironment
  2. createApplicationContext
  3. prepareContext

这三个步骤覆盖了应用上下文对象创建前的准备工作:创建环境信息对象,创建应用上下文对象本身,以及准备应用上下文。这个过程中,跟WebFlux Reactive Web紧密相关的要点如下 :

  1. this.webApplicationTypeREACTIVE,这一点在上面文章分析中已经明确;
  2. 环境信息对象的实现类根据this.webApplicationType 的值选用StandardReactiveWebEnvironment;
  3. 应用上下文对象的实现类根据this.webApplicationType 的值选用AnnotationConfigReactiveWebServerApplicationContext;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值