Spring Boot启动流程

一.前言

本文主要讲解spring boot应用启动的流程,看spring boot在启动过程中,如何实现以下几个非常重要的过程:

  1. 如何决定是web应用
  2. 创建spring环境
  3. 创建上下文

对于每个步骤的原理细节,本文不详细介绍。本文旨在探索宏观的流程控制。


二.整体流程

众所周知,Java应用的启动入口在于包含main方法的主类中,对于传统的web应用,包含main方法的主类由web容器实现。应用开发者只要遵循web开发规范,而不需要关心main方法主类。

spring boot应用恰好相反,spring boot应用的main方法主类由应用开发者编写,主要利用其SpringApplication快捷启动项目。

SpringApplication主要是帮助引导和启动spring boot应用,它本身提供很多配置方法用于设置spring应用。在启动过程中SpringApplication中主要完成以下几个步骤:

  1. 初始化SpringApplication
  2. 创建合适的spring环境
  3. 打印banner
  4. 依赖类路径创建合适的ApplicationContext
  5. 注册CommandLinePropertySource,作为命令行参数属性源
  6. 刷新上下文,载入单例Bean
  7. 以上的每个阶段,触发监听器的相应阶段
  8. 如果启动失败,进行失败分析

接下来看下SpringApplication的几个主步骤:

1.初始化SpringApplication
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
	if (sources != null && sources.length > 0) {
		this.sources.addAll(Arrays.asList(sources));
	}
	this.webEnvironment = deduceWebEnvironment();
	setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

这个initialize方法中主要是将配置的源类加入Set集合,决定是否为web环境、设置上下文初始化器、设置上下文监听器、mainApplicationClass。

2.SpringApplication run
public ConfigurableApplicationContext run(String... args) {
	// 创建StopWatch,用于统计spring boot应用的启动时间
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	// 定义applicationContext,失败分析器analyzers
	ConfigurableApplicationContext context = null;
	FailureAnalyzers analyzers = null;
	configureHeadlessProperty();
	// 加载SpringApplicationRunListener,主要用于监听spring boot应用run过程的每个主要阶段
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// 监听spring应用run过程的start开始阶段
	listeners.started();
	try {
		// 创建ApplicationArguments,抽象保存应用main方法参数
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(
				args);
		// 准备Environment
		ConfigurableEnvironment environment = prepareEnvironment(listeners,
				applicationArguments);
		// 打印Banner
		Banner printedBanner = printBanner(environment);
		// 创建ApplicationContext
		context = createApplicationContext();
		// 创建失败分析器
		analyzers = new FailureAnalyzers(context);
		// 上下文准备,主要是配置上下文。刷新的前置
		prepareContext(context, environment, listeners, applicationArguments,
				printedBanner);
		// 刷新上下文,就是加载bean
		refreshContext(context);
		// 刷新的后置
		afterRefresh(context, applicationArguments);
		// 监听spring应用run过程的finish完成阶段
		listeners.finished(context, null);
		// stopWatch停止,代表spring应用启动完成,完成启动的时间统计
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass)
					.logStarted(getApplicationLog(), stopWatch);
		}
		return context;
	}
	catch (Throwable ex) {
		handleRunFailure(context, listeners, analyzers, ex);
		throw new IllegalStateException(ex);
	}
}

以上的run方法是spring boot应用启动的核心流程,启动的逻辑控制完全在该run中实现。

接下来就细细剖析其中的每个阶段。


三.如何决定Web应用

spring boot是根据类路径是否存在web应用的标志类来判断是否为web应用。所以,这点对于日常开发项目时,maven的依赖要求非常严格,务必不能混乱引用依赖,同事也要注意依赖传递等。

private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
		"org.springframework.web.context.ConfigurableWebApplicationContext" };

如果类路径上有以上两个类,spring boot则认为该应用是为web应用。

private boolean deduceWebEnvironment() {
	for (String className : WEB_ENVIRONMENT_CLASSES) {
		if (!ClassUtils.isPresent(className, null)) {
			return false;
		}
	}
	return true;
}

如果Servlet和ConfigurableWebApplicationContext都在类路径上,即认为是web项目。


四.创建spring环境

private ConfigurableEnvironment prepareEnvironment(
		SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments) {
	// 创建spring环境
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	// 配置spring环境
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	// 触发监听器环境准备阶段
	listeners.environmentPrepared(environment);
	if (isWebEnvironment(environment) && !this.webEnvironment) {
		environment = convertToStandardEnvironment(environment);
	}
	return environment;
}

spring环境准备由SpringApplication中的prepareEnvironment完成,其中抉择环境时,会根据是否为web应用选择不同的环境类型:

  • StandardEnvironment:标准的spring环境,其中包含了profiles和spring propertySources
  • StandardServletEnvironment:web spring环境,是StandardEnvironment的扩展,其中增加了servletContext propertySource和servletConfig propertySource

其中配置spring环境时,会将命令行的参数和prfile解析进入spring环境的propertySource和profiles中。


五.创建上下文

protected ConfigurableApplicationContext createApplicationContext() {
	// 如果SpringApplication中的applicationContextClass不为空,直接返回applicationContextClass
	Class<?> contextClass = this.applicationContextClass;
	if (contextClass == null) {
		try {
			// 依据web环境,抉择上下文类型
			contextClass = Class.forName(this.webEnvironment
					? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalStateException(
					"Unable create a default ApplicationContext, "
							+ "please specify an ApplicationContextClass",
					ex);
		}
	}
	// 格局类型,反射实例化上下问类
	return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}

spring boot抉择上下文由SpringApplication中的createApplicationContext实现,其中只有两种上下文形式:

  • DEFAULT_WEB_CONTEXT_CLASS:嵌入式的web上下文AnnotationConfigEmbeddedWebApplicationContext。该上下文由spring boot实现,其中包含了嵌入式容器的创建,初始化等逻辑

  • DEFAULT_CONTEXT_CLASS:注解式配置上下文AnnotationConfigApplicationContext,该上下文是spring框架中实现,主要用于注解式配置的spring应用

posted @ 2020-03-17 16:48  怀瑾握瑜XI  阅读(338)  评论(0编辑  收藏  举报