Spring Boot源码解析.0.overview

本文详细解析了SpringBoot应用的启动过程,从主入口SpringApplication.run()开始,逐步剖析了构造器中的环境推测、初始化工厂加载、监听器加载以及启动过程中环境准备、上下文创建、刷新、事件发布等关键步骤,帮助读者深入理解SpringBoot的内部工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

工作如何繁忙,生活如何糟心,至少求知的这一刻是我的…

前言

基于Spring Boot 2.3.1。

从入口开始拆轮

拆轮子可以从它的入口方法开始拆起,先通读再细致地看。Spring Boot的入口已经是老生常谈了:

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

从SpringApplication#run方法进去,看它做了什么:

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

先看看SpringApplication(primarySources)的实现,该方法的实现实际调用了其重载方法。

public SpringApplication(Class<?>... primarySources) {
	this(null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// 推测web应用环境
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	// 加载Spring自身的初始化工厂方法
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	// 加载应用监听器
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 获取启动main方法的所在类
	this.mainApplicationClass = deduceMainApplicationClass();
}

可以看到,SpringApplication的构造器做了以下操作,通读即可,如需深入了解可自行查看源码,多打断点:

  • 推测web应用环境:通过反射获取classpath中的web应用环境,没有ClassNotFound,从而推测出是否为web应用;
  • 加载Spring自身的初始化工厂方法:读取spring-boot工程的spring.factories资源文件,从中获取到ApplicationContextInitalizer.class的各个实现类;
  • 加载应用监听器:同上读取,从中获取到ApplicationListener.class的各个实现类;
  • 获取启动main方法的所在类:通过主动抛出异常,遍历其异常栈直到异常栈的方法名为main,获取此时的类名。

接着看run方法做了什么:

public ConfigurableApplicationContext run(String... args) {
    // stopWatch,Spring实现的任务监控,在引入Spring的工程中需要监控某些方法运行时间可以用它,没必要自己实现了。
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	// Spring Boot启动错误回调报告器。
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
	// 配置java.awt.headless属性为true,如果当前运行设备缺失显示器、键鼠等外设,又需要相应外设的运算能力,那么需要配置该属性为true。
	// 比如服务器基本没有显示器,如果需要输出终端日志信息,那么就要开启它。
	configureHeadlessProperty();
	// 读取spring.factories,获取SpringApplicationRunListener.class的实现类,并一一实例化,目前仅一个实现类。
	// 即EventPublishingRunListener,用于处理Spring刷新上下文之前的事件,见下边的刷新上下文refreshContext(context);。
	// 这里可以打开思路,可以在Spring Boot真正启动前,通过发布一些事件,来做一些前处理。
	SpringApplicationRunListeners listeners = getRunListeners(args);
	listeners.starting();
	try {
	    // 缓存入参并提供对入参的一定解析能力。
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// 准备环境,包括web环境、解析入参、确认生效的profiles。
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		// 确认入参中是否配置了spring.beaninfo.ignore, 默认不配置。详见参考文献[1]。
		configureIgnoreBeanInfo(environment);
		/**
		 * 打印Banner,一般打印以下默认Banner。
		 *  .   ____          _            __ _ _
		 * /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
		 * ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
		 * \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
		 *  '  |____| .__|_| |_|_| |_\__, | / / / /
		 *  =========|_|==============|___/=/_/_/_/
		 * :: Spring Boot ::        (v2.3.1.RELEASE)
		**/
		Banner printedBanner = printBanner(environment);
		// 创建应用上下文,如包含web应用上下文,那么此时会通过反射创建;如果没有包含web应用上下文,那么就创建默认的上下文。
		context = createApplicationContext();
		// 读取spring.factories中的配置,获取SpringBootExceptionReporter.class的实现类
		exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);
		// 准备上下文
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		// 刷新Spring上下文
		refreshContext(context);// 无实现,有需要可继承SpringApplication.class覆盖该方法。
		afterRefresh(context, applicationArguments);
		// 停止监控,输出如:
		// c.l.blog.NuvoleBiancheApplicationMain    : Started DemoApplicationMain in 13.35 seconds (JVM running for 26.514)
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		// 向所有监听器发布ApplicationStartedEvent。
		listeners.started(context);
		// 将ApplicationRunner和CommandLineRunner一一运行起来。
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
	    // 向exceptionReporters报告异常
		handleRunFailure(context, ex, exceptionReporters, listeners);
		throw new IllegalStateException(ex);
	}

	try {
	    // 向所有监听器发布ApplicationReadyEvent。
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, exceptionReporters, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

解析prepareContext见这篇;
解析refreshContext见这篇。

参考文献

[1] https://docs.spring.io/spring-framework/docs/4.2.0.RELEASE/javadoc-api/org/springframework/beans/CachedIntrospectionResults.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值