【SpringBoot】SpringBoot 生命周期及相关事件浅析 一

前言

阅读本系列章节前,建议复习一下 Spring事件事件监听器事件发布 相关

【源码】Spring —— ApplicationEvent ApplicationListener ApplicationEventMulticaster

SpringBoot 提供了事件父类 SpringApplicationEvent,继承自 ApplicationEvent

SpringApplicationEvent
其下定义了 7 个事件,本系列结合部分源码解读 SpringBoot 何时发布这些 事件?对应的 事件监听器 都做了什么?

版本

SpringBoot 2.3.x

SpringApplication

SpringApplicationSpring 应用的抽象。在 SpringBoot 中,我们可以通过一个简单的 main 方法,来启动一个 Spring 应用

SpringApplication 启动

	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }

即创建一个 SpringApplication 实例,调用 run 方法

SpringApplication 实例构造

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		
		// ResourceLoader
		this.resourceLoader = resourceLoader;

		// 资源类
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

		// 依据 classpath 推断容器类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();

		// 从 spring.factories 加载所有 BootstrapRegistryInitializer
		this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();

		// 从 spring.factories 加载所有 ApplicationContextInitializer
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

		// 从 spring.factories 加载所有 ApplicationListener
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

		// 推断主类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

属性的初始化,其中 initializerslistenersspring.factories 文件读取加载

SpringApplication.run

	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();

		/**
		 * 创建 DefaultBootstrapContext
		 * 会基于 bootstrapRegistryInitializers 初始化
		 */
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;

		// 属性 java.awt.headless 处理
		configureHeadlessProperty();

		/**
		 * 构造 SpringApplicationRunListeners:
		 * 主要是就是用来发布各种 SpringApplicationEvent
		 */
		SpringApplicationRunListeners listeners = getRunListeners(args);

		// 发布 ApplicationStartingEvent
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			// 基于 args 构造 ApplicationArguments,即参数行命令
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

			/**
			 * 构造对应的 ConfigurableEnvironment 返回:
			 * 其中发布了核心事件 ApplicationEnvironmentPreparedEvent
			 * 		对应监听器完成了配置文件的读取解析
			 */
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

			// spring.beaninfo.ignore 属性相关
			configureIgnoreBeanInfo(environment);

			// 打印 banner 并返回 Banner 对象
			Banner printedBanner = printBanner(environment);

			// 根据 webApplicationType 创建对应的容器
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);

			/**
			 * 在执行完所有 ApplicationContextInitializer 后
			 * 		发布事件 ApplicationContextInitializedEvent
			 * 在加载完所有资源类后发布事件 ApplicationPreparedEvent
			 */
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

			/**
			 * 调用容器的 refresh 方法,单例会在该阶段后期创建
			 */
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}

			/**
			 * 发布 ApplicationStartedEvent
			 */
			listeners.started(context);

			/**
			 * 执行所有 CommandLineRunner 和 ApplicationRunner
			 */
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			/**
			 * 执行完所有 CommandLineRunner 和 ApplicationRunner 后,
			 * 发布事件 ApplicationReadyEvent
			 */
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

此处先贴出 run 方法的所有代码,做一个小总结:

  • SpringBoot 是基于事件驱动来管理拓展 SpringApplication 的,重要的事件如下:
  • 1)在创建好 DefaultBootstrapContextSpringApplicationRunListeners 后,发布第一个事件 ApplicationStartingEvent
  • 2)构造 ConfigurableEnvironmentEnvironmentSpringBoot 最核心的模块之一,在该过程中,会发布事件 ApplicationEnvironmentPreparedEvent
  • 3)创建对应的容器(ApplicationContext),执行对应的 ApplicationContextInitializer 后,发布事件 ApplicationContextInitializedEvent
  • 4)加载完对应资源类的 BeanDefinition 后,发布事件 ApplicationPreparedEvent
  • 5)执行完容器的 refresh 方法后,发布事件 ApplicationStartedEvent
  • 6)执行所有 CommandLineRunnerApplicationRunner 后,发布事件 ApplicationReadyEvent
  • 7)启动失败时发布事件 ApplicationFailedEvent
  • 针对上述事件,就可以提供对应的监听器,在恰当的时机进行功能拓展,比如配置文件的解析等,接下来就针对对应事件的行为及其对应的监听器进行了解

ApplicationStartingEvent

	/**
	 * 加载对应的 SpringApplicationRunListener
	 * 		封装成 SpringApplicationRunListeners
	 */
	SpringApplicationRunListeners listeners = getRunListeners(args);

	/**
	 * SpringApplicationRunListener#starting,
	 * 		此处发布 ApplicationStartingEvent
	 */
	listeners.starting();

	------------ EventPublishingRunListener ------------

	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}
  • 这里实例化了 SpringApplicationRunListenersSpringApplicationRunListeners 就是对内部 SpringApplicationRunListener 的一层封装,之后 SpringApplication 生命周期相关的 事件 将由 SpringApplicationRunListeners 来发布,其本质就是循环调用内部所有 SpringApplicationRunListener 对应方法
  • SpringApplicationRunListener 就是一个模板接口,定义了 SpringApplication 生命周期要发布 事件 的模板,SpringBoot 提供了一个实现类 EventPublishingRunListener,在上述代码的 getRunListeners 方法中,自动装配并创建 EventPublishingRunListener 的示例返回
  • EventPublishingRunListener 发布 事件 是委托给内部的 SimpleApplicationEventMulticaster 来实现的,即就是将 事件 发布给对应 事件监听器
  • 这里发布了 ApplicationStartingEvent 事件

ApplicationStartingEvent 对应的监听器

监听 ApplicationStartingEvent 的监听器有

  • LoggingApplicationListener:日志相关
  • BackgroundPreinitializer:针对 ApplicationStartingEvent 无动作
  • DelegatingApplicationListener:针对 ApplicationStartingEvent 无动作
  • LiquibaseServiceLocatorApplicationListenerliquibase 相关

ApplicationEnvironmentPreparedEvent


	---------------- prepareEnvironment ----------------

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		
		// 创建对应的 ConfigurableEnvironment
		ConfigurableEnvironment environment = getOrCreateEnvironment();

		/**
		 * 设置 convsersionService
		 * 基于参数行命令来填充 environment
		 */
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);

		// ApplicationEnvironmentPreparedEvent
		listeners.environmentPrepared(bootstrapContext, environment);

		// 将 defaultProperties 移到末尾(优先级最低)
		DefaultPropertiesPropertySource.moveToEnd(environment);

		// additionalProfiles 属性生效
		configureAdditionalProfiles(environment);

		/**
		 * spring.main 相关属性绑定
		 */
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

	------------ EventPublishingRunListener ------------

	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster
				.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
	}
  • 首先创建了 ConfigurableEnvironment 实例
  • 初步配置后,发布 ApplicationEnvironmentPreparedEvent 事件,该 事件 相关的 监听器 动作比较大,SpringBoot 强大的配置能力也基于此实现
  • 后续动作,比如绑定相关配置到当前 SpringApplication 实例,之后返回配置好的 ConfigurableEnvironment 实例

ApplicationEnvironmentPreparedEvent 对应的监听器

  • ConfigFileApplicationListener:在这个 监听器 中,自动装配其他 4EnvironmentPostProcessorConfigFileApplicationListener 本身也是一个 EnvironmentPostProcessor),然后依次调用了它们的 postProcessEnvironment 方法
  • 1)SystemEnvironmentPropertySourceEnvironmentPostProcessor:将 environment 中名为 systemEnvironmentPropertySource 替换为 OriginAwareSystemEnvironmentPropertySource 类型的(可调用 getOrigin 方法获取对应的 SystemEnvironmentOrigin
  • 2)SpringApplicationJsonEnvironmentPostProcessor:解析 environment 所有 PropertySource 中的 spring.application.jsonSPRING_APPLICATION_JSON 属性
  • 3)CloudFoundryVcapEnvironmentPostProcessorSpringCloud 相关
  • 4)ConfigFileApplicationListener:该监听器本身也是最核心的 EnvironmentPostProcessor:给 environment 添加一个名为 randomPropertySource;加载 environmentactiveProfiles 属性;加载配置文件的所有配置到 environmentpropertySources
  • 5)DebugAgentEnvironmentPostProcessorSpring Reactor 相关
  • AnsiOutputApplicationListener:解析 spring.output.ansi.enabled 属性
  • LoggingApplicationListener:日志相关
  • ClasspathLoggingApplicationListener:日志输出 classpath 信息
  • BackgroundPreinitializer:启动一个线程异步执行 实例化DefaultFormattingConversionService实例化AllEncompassingFormHttpMessageConverter 等动作,该行为可以通过 spring.backgroundpreinitializer.ignore 属性控制
  • DelegatingApplicationListener:解析 context.listener.classes 属性对应的所有自定义的 事件监听器,在这之后的事件也会同样发布给这些 监听器。换句话说,用户可以 自定义监听器 监听 ApplicationEnvironmentPreparedEvent 及其之后的所有 ApplicationEvent 事件(注意,监听不到 ApplicationStartingEvent
  • FileEncodingApplicationListener:如果设置了 spring.mandatory-file-encoding 属性,则当其与系统的 file.encoding 属性不同时,输出相关日志信息

总结

本章节介绍了 ApplicationStartingEvent ApplicationEnvironmentPreparedEvent生命周期 动作,以及对应 监听器 的行为,避免篇幅太长,下章节继续

关于配置文件加载到 Environment 相关,在 2.4.x 版本进行了不兼容改动,更多细节可参考下文:

【SpringBoot】对比 SpringBoot 2.4.0 版本前后配置文件机制改动

下一篇:【SpringBoot】SpringBoot 生命周期及相关事件浅析 二

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值