深刻理解SpringBoot核心原理(三)-------- run()启动源码全过程分析

1、前言

  在上一篇咱们了解到 new SpringApplication(primarySources)实例初始化源码的加载过程,经过走跟源码分析了基本初始化过程以下:
html

1.资源初始化资源加载器为 nulljava

2.断言主要加载资源类不能为 null,不然报错spring

3.初始化主要加载资源类集合并去重并发

4.推断当前 WEB 应用类型app

5.设置应用上下文初始化器less

6.设置监听器spring-boot

7.推断主入口应用类 源码分析

若是,各位同窗有遗忘的,能够去复习一下上篇文章深刻理解SpringBoot核心原理(二)--------初始化流程(run方法)。 那么,这篇咱们继续往下面分析其核心 run 方法。post

2、SpringApplication 实例 run 方法运行过程


下面继续来分析SpringApplication对象的run方法实现过程以及运行原理。

仍是跟以前的分析流程同样,先来看一下run方法里面整体的流程实现:学习

public ConfigurableApplicationContext run(String... args) {
		// 一、建立并启动计时监控类
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		
		// 二、初始化应用上下文和异常报告集合
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		
		// 三、设置系统属性 `java.awt.headless` 的值,默认值为:true
		configureHeadlessProperty();
		
		// 四、建立全部 Spring 运行监听器并发布应用启动事件
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			
			// 五、初始化默认应用参数类
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			
			// 六、根据运行监听器和应用参数来准备 Spring 环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			
			// 七、建立 Banner 打印类
			Banner printedBanner = printBanner(environment);
			
			// 八、建立应用上下文
			context = createApplicationContext();
			
			// 九、准备异常报告器
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			
			// 十、准备应用上下文
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			
			// 十一、刷新应用上下文
			refreshContext(context);
			
			// 十二、应用上下文刷新后置处理
			afterRefresh(context, applicationArguments);
			
			 // 1三、中止计时监控类
			stopWatch.stop();
			
			// 1四、输出日志记录执行主类名、时间信息
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			
			// 1五、发布应用上下文启动完成事件
			listeners.started(context);
			
			// 1六、执行全部 Runner 运行器
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			
			// 1七、发布应用上下文就绪事件
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		// 1八、返回应用上下文
		return context;
	}
复制代码

3、run 方法运行过程分解

3.1 建立并启动计时监控类

StopWatch stopWatch = new StopWatch();
		stopWatch.start();
复制代码

进入start()方法以下:

/** * Start an unnamed task. The results are undefined if {@link #stop()} * or timing methods are called without invoking this method. * @see #stop() */
	public void start() throws IllegalStateException {
		start("");
	}

	/** * Start a named task. The results are undefined if {@link #stop()} * or timing methods are called without invoking this method. * @param taskName the name of the task to start * @see #stop() */
	public void start(String taskName) throws IllegalStateException {
		if (this.currentTaskName != null) {
			throw new IllegalStateException("Can't start StopWatch: it's already running");
		}
		this.currentTaskName = taskName;
		this.startTimeMillis = System.currentTimeMillis();
	}
复制代码

首先记录了当前任务的名称,默认为空字符串,而后记录当前 Spring Boot 应用启动的开始时间。

3.2 初始化应用上下文和异常报告集合

ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
复制代码

3.3 设置系统属性 java.awt.headless 的值

configureHeadlessProperty();
复制代码

至于为何设置这个属性值为true,能够参考下面这篇文章:www.cnblogs.com/princessd82…

3.4 建立全部 Spring 运行监听器并发布应用启动事件

SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
复制代码

进去看一下建立spring运行监听器的相关源码:

private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}
SpringApplicationRunListeners {
        ......
		SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}
        ......
}
复制代码

建立逻辑和以前实例化初始化器和监听器的同样,同样调用的是getSpringFactoriesInstances 方法来获取配置的监听器名称并实例化全部的类。SpringApplicationRunListener全部监听器配置在 spring-boot-2.0.4.RELEASE.jar!/META-INF/spring.factories 这个配置文件里面:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
复制代码

3.5 初始化默认应用参数类

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
复制代码

3.6 根据运行监听器和应用参数来准备 Spring 环境

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
复制代码

下面咱们主要来看下准备环境的 prepareEnvironment 源码:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
		// 1.Create the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 2.Configure the 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;
	}
复制代码

3.7 建立 Banner 打印类

Banner printedBanner = printBanner(environment);
复制代码

3.8 建立应用上下文

context = createApplicationContext();
复制代码

进去源码,能够知道这里主要是根据不一样的应用类型初始化不一样的上下文应用类。

3.9 准备异常报告器

exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
复制代码

getSpringFactoriesInstances ------>>createSpringFactoriesInstances ------->>>逻辑和以前实例化初始化器和监听器的同样,同样调用的是 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化全部的异常处理类。
该异常报告处理类配置在 spring-boot-2.0.4.RELEASE.jar!/META-INF/spring.factories 这个配置文件里面。

# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
复制代码

3.10 准备应用上下文

prepareContext(context, environment, listeners, applicationArguments, printedBanner);
复制代码

接下来进入prepareContext方法:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		// 配置上下文的 bean 生成器及资源加载器
		postProcessApplicationContext(context);
		// 为上下文应用全部初始化器
		applyInitializers(context);
		// 触发全部 SpringApplicationRunListener 监听器的 contextPrepared 事件方法
		listeners.contextPrepared(context);
		// 记录日志
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot specific singleton beans 启动两个特殊的单例bean
		context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources 加载全部资源
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		// 触发全部 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
		listeners.contextLoaded(context);
	}
复制代码

3.11 刷新应用上下文

refreshContext(context);
复制代码

3.12 应用上下文刷新后,自定义处理

afterRefresh(context, applicationArguments);

	protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
	}
复制代码

3.13 中止计时监控类

stopWatch.stop();
复制代码

计时监听器中止,并统计一些任务执行信息。

3.14 输出日志记录执行主类名、时间信息

if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
复制代码

3.15 发布应用上下文启动完成事件

listeners.started(context);
复制代码

这里会触发全部 SpringApplicationRunListener 监听器的 started 事件方法。

3.16 执行全部 Runner 运行器

callRunners(context, applicationArguments);
复制代码

执行全部ApplicationRunner以及CommandLineRunner执行器

3.17 发布应用上下文就绪事件

listeners.running(context);
复制代码

触发全部 SpringApplicationRunListener 监听器的 running 事件方法。

3.18 返回应用上下文

return context;
复制代码

4、总结

  关于SpringBootApplication.run()启动实例初始化以及实例加载run方法的源码分析到此结束,分析源码是件有点痛苦的事情,不过度析完源码后,你会对SpringBoot是如何加载以及初始化有更全面的了解,固然其中也有其它的一些东西值得学习,好比Spring事件监听,如何使用单例,自动化配置等等,最后,但愿给各位同窗在学习SpringBoot的路上提供一点帮助。看完,若是以为有收获,但愿点个赞。

相关文章
相关标签/搜索