spring boot源码解析(一)

题外话

因工作原因,陆续开始接触spring boot,且对其源码和机制越来越发的需要深入了解。所以想系统的分析下spring boot源码,希望可以坚持下去。

spring boot启动

@SpringBootApplication
public class SimpleApplication {

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

}

从最简化的spring boot启动main入口中可以看出通过SpringApplication.run(Class, String[])方法启动的。我们跟进入方法,不难发现run静态方法中构造了SpringApplication对象,然后进行run方法。
首先查看主构造方法:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
}
  1. resourceLoader从注解来看是一个资源加载loader,这里传入的是null,应该后续会使用到默认值。
  2. primarySources传入的是我们main方法的启动类,从这里可以看出能传入多个,这里我们传入的就一个。
  3. webApplicationType表示web服务类型,从这个枚举类内部代码可以看出有none,servlet,reactive三种,根据依赖包来进行判断的。
  4. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)) 简单理解获取了ApplicationContextInitalizer的实现。
  5. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)) 简单理解获取ApplicationListener的实现。
  6. mainApplicationClass通过deduceMainApplicationClass方法获取main启动类(异常类堆栈的方式),这里可以看出main启动类和primarySources对应的类可以不一样。

前面讲到了两个接口:ApplicationContextInitializer和ApplicationListener,这两个接口分别是干什么的呢。简单介绍一下:
ApplicationContextInitializer通过接口注解了解到:

  • 用于在ConfigurableApplicationContext执行refresh操作之前对它进行一些初始化操作
  • 通常被用作web应用,在一些程序设计在spring容器初始化使用。
  • 支持Spring中的Ordered接口以及@Order注解来对多ApplicationContextInitializer实例进行排序,按照排序后的顺序依次执行回调

ApplicationListener基于观察者模式实现的一种application程序启动过程中观察程序状态过程变更的一种机制,可以自定义实现ApplicationListener用于观察ApplicationEvent或者某种Event,甚至可以自定义Event,然后手动出发publishEvent。(有点像guava的EventBus机制,只是这里落地到场景

run

我们接下来看下SpringApplication对象的方法run()

	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);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			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);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			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;
	}
  1. StopWatch简单的看成一个stop watch的机制,保存stop的记录信息。
  2. configureHeadlessProperty即配置headless模式,这种模式是一种系统缺少显示设备、键盘和鼠标外设的情况模式。
  3. SpringApplicationListeners为SpringApplicationRunListener接口实现集合,可以理解这个接口就是在spring启动整个过程都需要回调这些listener,debug能发现,拿到了一个名为EventPublishingRunListener,这个就是用来进行触发publishEvent的被观察者。
  4. ConfigurableEnvironment为配置环境对象,简单理解所有的配置信息汇总在这个对象中
  5. Banner就是我们常在控制台输出的画面横幅,可以使用图片或者文本进行替换
  6. ConfigurableApplicationContext根据webApp…Type进行构造的上下文对象
  7. exceptionReporters为实现SpringBootExceptionReporter对象集合,可以自定义实现异常上报收集的实现等,spring-boot中有个FailureAnalyzers分析。
  8. 接下来进入关键步骤的第一步:prepareContext,准备容器阶段,将执行所有的initializers逻辑,做初始化准备操作。
  9. refreshContext,可以理解成容器初始化节点,将执行bean的创建和实例化。
  10. afterRefresh,容器后处理, 可以看到会找到ApplicationRunner和CommandLineRunner的实现类并执行。但从2.x版本来看,似乎这个方法是个空方法,applicationRun和commandRun移到启动最后。
  11. 然后根据stopwatch打印出启动时间
  12. 这里调用ApplicationRunner和CommandLineRunner的实现类

结语

其实到这里我们简单的分析了下,SpringApplication.run作为spring boot启动的时候的动作过程。后面我们将陆续展开讨论内部的一些过程。

猜你喜欢

转载自blog.csdn.net/caohao1210/article/details/88550240