SpringBoot应用启动过程简单分析

今天看了Springboot的启动过程,自己分析了一下(有什么不足请指出)。
** 本文主要是 2.0.2.RELEASE 的源码 **
SpringBoot项目通过SpringApplication.run(App.class, args)来启动:

@SpringBootApplication
public class App {
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

接下来,通过源码来看看SpringApplication.run()方法的执行过程。
1、 调用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);
}

2、SpringApplication对象初始化

public SpringApplication(Class<?>... primarySources) {
	this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	// 拿到资源加载器
	this.resourceLoader = resourceLoader;
	// 通过“断言” 判断 primarySources 是否为空
	Assert.notNull(primarySources, "PrimarySources must not be null");
	// 使用Set确保唯一性以防止重复
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// 判断是否为WEB环境
	this.webApplicationType = deduceWebApplicationType();
	// 找到META-INF/spring.factories中ApplicationContextInitializer所有实现类,并将其实例化
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	// 找到META-INF/spring.factories中ApplicationListener所有实现类,并将其实例化
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 获取当前main方法类对象,即测试类中的App实例
	this.mainApplicationClass = deduceMainApplicationClass();
}

对象初始化过程中,使用到了getSpringFactoriesInstances方法:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
	return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
	// 获取 当前线程类加载器
	ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
	// 使用Set确保唯一性以防止重复
	// Use names and ensure unique to protect against duplicates
	// 读取META-INF/spring.factories指定接口的实现类
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	AnnotationAwareOrderComparator.sort(instances);
	return instances;
}
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) {
	List<T> instances = new ArrayList<>(names.size());
	for (String name : names) {
		try {
			Class<?> instanceClass = ClassUtils.forName(name, classLoader);
			Assert.isAssignable(type, instanceClass);
			Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
			T instance = (T) BeanUtils.instantiateClass(constructor, args);
			instances.add(instance);
		} catch (Throwable ex) {
			throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
		}
	}
	return instances;
}

3、通过setInitializers来完成, ApplicationContextInitializer应用程序初始化器,做一些初始化的工作:

public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
	this.initializers = new ArrayList<>();
	this.initializers.addAll(initializers);
}
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
	// 实例化
	void initialize(C applicationContext);
}

4、通过setListeners来完成, ApplicationListener应用程序初始化器,做一些初始化的工作:

public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
	this.listeners = new ArrayList<>();
	this.listeners.addAll(listeners);
}
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
	void onApplicationEvent(E event);
}

这里的应用程序事件(ApplicationEvent)有应用程序启动事件(ApplicationStartedEvent),失败事件(ApplicationFailedEvent),准备事件(ApplicationPreparedEvent)等。

应用程序事件监听器跟监听事件是绑定的。比如ConfigServerBootstrapApplicationListener只跟ApplicationEnvironmentPreparedEvent事件绑定,LiquibaseServiceLocatorApplicationListener只跟ApplicationStartedEvent事件绑定,LoggingApplicationListener跟所有事件绑定等。

默认情况下,initialize方法从spring.factories文件中找出的key为ApplicationContextInitializer的类有:

org.springframework.boot.context.config.DelegatingApplicationContextInitializer
org.springframework.boot.context.ContextIdApplicationContextInitializer
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

key为ApplicationListener的有:

org.springframework.boot.context.config.ConfigFileApplicationListener
org.springframework.boot.context.config.AnsiOutputApplicationListener
org.springframework.boot.logging.LoggingApplicationListener
org.springframework.boot.logging.ClasspathLoggingApplicationListener
org.springframework.boot.autoconfigure.BackgroundPreinitializer
org.springframework.boot.context.config.DelegatingApplicationListener
org.springframework.boot.builder.ParentContextCloserApplicationListener
org.springframework.boot.context.FileEncodingApplicationListener
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

SpringApplication的执行
分析run方法之前,先看一下SpringApplication中的一些事件和监听器概念。
首先是SpringApplicationRunListeners类和SpringApplicationRunListener类的介绍。
SpringApplicationRunListeners内部持有SpringApplicationRunListener集合和1个Log日志类。用于SpringApplicationRunListener监听器的批量执行。
SpringApplicationRunListener看名字也知道用于监听SpringApplication的run方法的执行。

它定义了5个步骤:

  1. started(run方法执行的时候立马执行;对应事件的类型是ApplicationStartedEvent)
  2. environmentPrepared(ApplicationContext创建之前并且环境信息准备好的时候调用;对应事件的类型是ApplicationEnvironmentPreparedEvent)
  3. contextPrepared(ApplicationContext创建好并且在source加载之前调用一次;没有具体的对应事件)
  4. contextLoaded(ApplicationContext创建并加载之后并在refresh之前调用;对应事件的类型是ApplicationPreparedEvent)
  5. finished(run方法结束之前调用;对应事件的类型是ApplicationReadyEvent或ApplicationFailedEvent)

SpringApplicationRunListener目前只有一个实现类EventPublishingRunListener,它把监听的过程封装成了SpringApplicationEvent事件并让内部属性(属性名为multicaster)ApplicationEventMulticaster接口的实现类SimpleApplicationEventMulticaster广播出去,广播出去的事件对象会被SpringApplication中的listeners属性进行处理。

所以说SpringApplicationRunListener和ApplicationListener之间的关系是通过ApplicationEventMulticaster广播出去的SpringApplicationEvent所联系起来的。

SpringApplicationRunListener

	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch(); // 构造一个任务执行观察器
		stopWatch.start();// 开始执行,记录开始时间
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 设置headless系统变量  
		configureHeadlessProperty();
		// 启动SpringApplicationRunListener监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 启动所有的监听
		listeners.starting();
		try {
			// 实例化ApplicationArguments对象 构造一个应用程序参数持有类
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			// 配置environment		
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			// 配置 IgnoreBeanInfo
			configureIgnoreBeanInfo(environment);
			// 生成 Banner
			Banner printedBanner = printBanner(environment);
			// 创建Spring容器
			context = createApplicationContext();
			// 加载spring.factories中的SpringBootExceptionReporter实现类
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			// 创建、装置、刷新、运行ApplicationContext
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			// 加载或刷新持久化形式的配置(如xml文件、properties文件,和数据库信息)。
			refreshContext(context);
			// 容器创建完成之后执行额外一些操作
			afterRefresh(context, applicationArguments);
			stopWatch.stop();// 执行结束,记录执行时间
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			// 广播出ApplicationReadyEvent事件给相应的监听器执行
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners); // 这个过程报错的话会执行一些异常操作、然后广播出ApplicationFailedEvent事件给相应的监听器执行
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;// 返回Spring容器
	}

通过对getRunListeners的跟踪,你会发现SpringApplicationRunListener是这样的

public interface SpringApplicationRunListener {
	/**
	 * 通知监听器,SpringBoot开始启动
	 */
	void started();
	/**
	 * 通知监听器,环境配置完成
	 */
	void environmentPrepared(ConfigurableEnvironment environment);
	/**
	 * 通知监听器,ApplicationContext已创建并初始化完成
	 */
	void contextPrepared(ConfigurableApplicationContext context);
	/**
	 * 通知监听器,ApplicationContext已完成IOC配置
	 */
	void contextLoaded(ConfigurableApplicationContext context);
	/**
	 * 通知监听器,SpringBoot开始完毕
	 */
	void finished(ConfigurableApplicationContext context, Throwable exception);
}

附图为ApplicationListener监听接口实现类,每个类对应了一种事件。
ApplicationListener

解读prepareEnvironment

	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		// 创建并配置 environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 配置一些环境信息。比如profile,命令行参数, 配置 environment 的 PropertySources 和 Profiles
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 广播出ApplicationEnvironmentPreparedEvent事件给相应的监听器执行
		listeners.environmentPrepared(environment);
		// 将environment绑定到SpringApplication上
		bindToSpringApplication(environment);
		// 根据webApplicationType创建一个ConfigurableEnvironment对象
		// 如果webApplicationType 是 WebApplicationType.SERVLET 或 WebApplicationType.REACTIVE 则创建其子类StandardServletEnvironment()对象,否则创建StandardEnvironment对象
		if (this.webApplicationType == WebApplicationType.NONE) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		// 在environment.propertySources中添加(如果没有的话)一个
		// ConfigurationPropertySourcesPropertySource对象,
		// 使得environment管理的PropertySource对象能适配 PropertySourcesPropertyResolver,
		// 能够通过属性名get到具体的配置
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

Spring容器的创建createApplicationContext方法如下:

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		// 如果是web程序,那么构造org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext容器
		// 如果是reactive ,那么构造 boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext容器
        // 否则构造org.springframework.context.annotation.AnnotationConfigApplicationContext容器
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					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);
	}

解读prepareContext

	private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment); // 设置Spring容器的环境信息
		postProcessApplicationContext(context); // 回调方法,Spring容器创建之后做一些额外的事
	    applyInitializers(context); // SpringApplication的的初始化器开始工作
		// 遍历调用SpringApplicationRunListener的contextPrepared方法。
		// 目前只是将这个事件广播器注册到Spring容器中
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot specific singleton beans
		// 把应用程序参数持有类注册到Spring容器中,并且是一个单例
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
		// 将beans载入到ApplicationContext容器中
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		// 通知监听器,beans载入ApplicationContext完毕
		listeners.contextLoaded(context);
	}

##总结##
spring boot通过扩展了jar协议,抽象出Archive概念,和配套的JarFile,JarUrlConnection,LaunchedURLClassLoader,从而实现了上层应用无感知的all in one的开发体验。尽管Executable war并不是spring提出的概念,但spring boot让它发扬光大。

  1. SpringBoot启动的时候,不论调用什么方法,都会构造一个SpringApplication的实例,然后调用这个实例的run方法,这样就表示启动SpringBoot。
  2. 在run方法调用之前,也就是构造SpringApplication的时候会进行初始化的工作,初始化的时候会做以下几件事:
    把参数sources设置到SpringApplication属性中,这个sources可以是任何类型的参数

判断是否是web程序,并设置到webEnvironment这个boolean属性中
找出所有的初始化器,默认有5个,设置到initializers属性中
找出所有的应用程序监听器,默认有9个,设置到listeners属性中
找出运行的主类(main class)
SpringApplication构造完成之后调用run方法,启动SpringApplication,run方法执行的时候会做以下几件事:

3.构造一个StopWatch,观察SpringApplication的执行
找出所有的SpringApplicationRunListener并封装到SpringApplicationRunListeners中,用于监听run方法的执行。监听的过程中会封装成事件并广播出去让初始化过程中产生的应用程序监听器进行监听
构造Spring容器(ApplicationContext),并返回
3.1 创建Spring容器的判断是否是web环境,是的话构造AnnotationConfigEmbeddedWebApplicationContext,否则构造AnnotationConfigApplicationContext
3.2 初始化过程中产生的初始化器在这个时候开始工作
3.3 Spring容器的刷新(完成bean的解析、各种processor接口的执行、条件注解的解析等等)
从Spring容器中找出ApplicationRunner和CommandLineRunner接口的实现类并排序后依次执行
启动过程框图

spring boot是一个惊人的项目,可以说是spring的第二春,spring-cloud-config, spring-session, metrics, remote shell等都是深爱开发者喜爱的项目、特性。几乎可以肯定设计者是有丰富的一线开发经验,深知开发人员的痛点。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漏墨小子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值