Spring Boot 源码--SpringApplication#run 方法源码解析

本文详细解读了SpringBoot项目中,`SpringApplication.run`方法的内部工作机制,涉及创建BootstrapContext、ConfigurableApplicationContext、监听器和环境配置等关键环节,揭示了SpringBoot自动装配背后的实现原理。
摘要由CSDN通过智能技术生成

前言:

开发过 Spring Boot 项目的都知道只需要一个简单的入口类,然后入口类中有个 main 方法,main 方法中调用了 SpringApplication.run 方法,再配合 @SpringBootApplication 注解就可以完成一个项目的启动,如下:

@SpringBootApplication
public class DemoApplication {

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

}

那你知道 SpringApplication#run 具体做了什么吗?

启动流程简单debugg:

在这里插入图片描述

SpringApplication#run 方法源码解析:

SpringApplication#run 方法:

//run 方法的封装 
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
	return run(new Class[]{primarySource}, args);
}
//创建 SpringApplication 对象 调用 SpringApplication 的 run 方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	return (new SpringApplication(primarySources)).run(args);
}

面两个方法,一个方法完成了 SpringApplication 创建,一个完成了 run 方法的调用,非常简单,具体的调用细节在下面的方法中。

真正的SpringApplication#run 方法:

//真正的SpringApplication run 方法
public ConfigurableApplicationContext run(String... args) {
	//计时器
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	//创建默认的引导上下文 实现了 ConfigurableBootstrapContext 接口
	DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
	//配置应用程序上下文
	ConfigurableApplicationContext context = null;
	//设置系统属性 java.awt.headless 的值 默认值为true 开启headless模式
	this.configureHeadlessProperty();
	//创建监听器  获取运行时的监听器列表
	SpringApplicationRunListeners listeners = this.getRunListeners(args);
	//启动监听器
	listeners.starting(bootstrapContext, this.mainApplicationClass);

	try {
		//初始化默认应用参数类
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		//根据监听器 引导上下文 默认参数 准备 spring 环境
		ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
		//设置需要忽略的 bean 信息 BeanInfo是一种描述 Java Bean 属性 事件 方法的元数据信息的机制
		this.configureIgnoreBeanInfo(environment);
		//创建打印类 可以自定义启动时候的打印信息
		Banner printedBanner = this.printBanner(environment);
		//创建应用程序上下文
		context = this.createApplicationContext();
		//应用程序上下文设置 applicationStartup
		context.setApplicationStartup(this.applicationStartup);
		//准备上下文--预处理 上面已经创建了应用程序上下文 但是还是一个空白对象 
		this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
		//刷新应用上下文 核心方法 spring 源码中会讲解
		this.refreshContext(context);
		//刷新应用上下文后置处理
		this.afterRefresh(context, applicationArguments);
		//即时结束
		stopWatch.stop();
		//日志
		if (this.logStartupInfo) {
			(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
		}
		//广播应用上下文加载完成
		listeners.started(context);
		//调用执行Runner  如果需要 spring boot 项目启动就执行某些操作就可以自定义实现  ApplicationRunner  CommandLineRunner
		this.callRunners(context, applicationArguments);
	} catch (Throwable var10) {
		this.handleRunFailure(context, var10, listeners);
		throw new IllegalStateException(var10);
	}

	try {
		//广播应用上下文运行
		listeners.running(context);
		return context;
	} catch (Throwable var9) {
		this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
		throw new IllegalStateException(var9);
	}
}

//准备应用程序上下文
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	//为应用程序上下文设置环境
	context.setEnvironment(environment);
	//应用程序上下文后处理
	this.postProcessApplicationContext(context);
	//初始化应用程序上下文
	this.applyInitializers(context);
	//监听器广播上下文已经准备好
	listeners.contextPrepared(context);
	//引导上下文关闭
	bootstrapContext.close(context);
	if (this.logStartupInfo) {
		this.logStartupInfo(context.getParent() == null);
		this.logStartupProfileInfo(context);
	}
	//获取应用程序上下文的beanFactory 并设置一些属性
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	//注册单例bean springApplicationArguments  容器指定的参数封装成 bean  注入容器
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		//设置单例bean
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}

	if (beanFactory instanceof DefaultListableBeanFactory) {
		//设置bean 定义允许覆盖
		((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}

	if (this.lazyInitialization) {
		//添加延迟加载bean 后置处理器
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}

	Set<Object> sources = this.getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	//加载bean对象到 context中
	this.load(context, sources.toArray(new Object[0]));
	//监听器广播上下文已经加载
	listeners.contextLoaded(context);
}

源码关键对象分析:

  • DefaultBootstrapContext:是 ConfigurableBootstrapContext 接口的实现,在 Spring Boot 应用程序的引导阶段使用,可以通过注册一些自定义的组件,去影响应用程序的启动行为,也是 Spring Boot 程序启动的一个扩展点。
  • ConfigurableApplicationContext:应用程序上下文可配置的接口,开发人员可以对应用程序上下文进行更灵活的配置和可扩展性,以满足不同应用程序的需求,例如设置环境、配置文件位置、添加后置处理器等。它提供了更多的灵活性和可扩展性,以满足不同应用程序的需求。
  • SpringApplicationRunListeners:监听器列表,负责在应用程序运行过程中触发相应的事件和回调函数,通过注册监听器,开发人员可以在应用程序启动、停止、失败等不同的阶段执行自定义的逻辑。
  • ApplicationArguments:实际用的是实现类 DefaultApplicationArguments,是一个参数解析类,ApplicationArguments 接口提供了对应用程序启动参数的访问和解析功能,DefaultApplicationArguments 对象可以方便地获取和解析应用程序启动时传入的参数信息,也可以自己根据需求去扩展。
  • ConfigurableEnvironment:环境配置接口,提供了一种可配置的方式来管理应用程序的配置属性,通过调用 prepareEnvironment(listeners, applicationArguments) 方法,可以根据传入的监听器和应用程序参数来准备环境配置,包括加载配置文件、解析配置等,同样可以根据具体的应用程序需求进行扩展。
  • Banner:接受一个 ConfigurableEnvironment 对象,通过 printBanner 方法,打印出自定义的 Banner(程序启动控制台打印的图标,比如打印一个:永无bug),这个功能没有什么大的作用,是一个提升体验感的小功能。
  • prepareContext:应用程序上下文启动预处理,包括设置环境变量、注册监听器、处理命令行参数等,保证应用程序上下文可以正常启动。
  • refreshContext:刷新应用程序上下文,主要解析配置文件,加载业务 bean等,底层调用的是 Spring 的 refresh 方法。
  • afterRefresh:刷新应用程序上下文后的操作,Spring Boot 没有对其进行实现,应用程序开发者有需要可以自定义一些自己逻辑。
  • listeners.started(context):通知注册的监听器应用程序已经启动完成。
  • this.callRunners(context, applicationArguments):执行应用程序的运行器,比如执行我们自定义的 Runner,例如初始化数据、启动定时任务等,这个还是比较常用的。
  • listeners.running(context):通知注册的监听器应用程序已经开始运行。

总结:习惯了 Spring Boot 的自动装配,一个简单的入口类,就可以帮我们跑起来应用程序,但是我们不知道是怎样启动的,具体都做了什么,阅读了源码之后,我们发现框架帮我们做了很多工作,也给我们留了很多可以扩展的入口,也有我们常用的功能在源码里都有体现,比如我们常用的 Runner 功能,可能在开发中没少用,但是有没有想过为什么可以这么用呢?其实这在源码中都有体现,如果你阅读了源码,就会对这些底层实现了然于胸,本文简单分享了 Spring Boot 启动相关源码,希望帮助到有需要的伙伴们。

欢迎提出建议及对错误的地方指出纠正。

  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值