SpringBoot3.X源码分析(启动流程)

这篇具有很好参考价值的文章主要介绍了SpringBoot3.X源码分析(启动流程)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

@SpringBootApplication(
    scanBasePackages = {"com.javaedge.base"}
)
public class BaseApplication {
    public BaseApplication() {
    }

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

1 启动入口

静态辅助类,可用于运行使用默认配置(即我们添加的一系列注解)的指定源的 SpringApplication 。

  • primarySource - 要载入的主要源,即指定源,这里为传入的Application.class Class\<?> :泛型决定了任何类都可以传入
  • args - 应用程序参数(通常从main方法传递)
  • 返回:正在运行的ApplicationContext
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
  return run(new Class<?>[] { primarySource }, args);
}
  • 1.
  • 2.
  • 3.
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
  return new SpringApplication(primarySources).run(args);
}
  • 1.
  • 2.
  • 3.

构造一个SpringApplication的实例,然后再调用这里实例的run方法就表示启动SpringBoot。因此,想要分析SpringBoot的启动过程,我们需要熟悉:

  • SpringApplication的构造过程
  • SpringApplication的run方法执行过程

2 SpringApplication的构造过程

创建新 SpringApplication 实例。应用程序上下文将从指定的主要源加载 bean。实例可以在调用 run(String...)之前自定义。

public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}
@SuppressWarnings({ "unchecked", "rawtypes" })
	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程序:
    // jakarta.servlet.Servlet
    // org.springframework.web.context.ConfigurableWebApplicationContext
    // 须都在类加载器中存在,并设置到webEnvironment属性
		this.webApplicationType = WebApplicationType.deduceFromClasspath();

    // 从 META-INF/spring.factories 中找K=ApplicationContextInitializer的类并实例化后
    // 设置到SpringApplication的initializers属性。即找出所有的应用程序初始化器
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

    // 从spring.factories文件中找出key为ApplicationListener的类并实例化后
    // 设置到SpringApplication的listeners属性。即找出所有的应用程序事件监听器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 找出main类,这里是 BaseApplication 类
		this.mainApplicationClass = deduceMainApplicationClass();
	}
ApplicationContextInitializer

SpringBoot3.X源码分析(启动流程),spring boot,java,开发语言,spring boot,源码

应用程序初始化器,做一些初始化的工作。

用于在刷新之前初始化 Spring ConfigurableApplicationContext 的回调接口。 通常用于需要对应用程序上下文进行某种编程初始化的 Web 应用程序中。如针对上下文的环境注册属性源或激活配置文件。

ApplicationContextInitializer 鼓励处理器检测 Spring 的 Ordered 接口是否已实现或 @Order 注解是否存在,并在调用之前对实例进行相应的排序(如果有)

@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

	/**
	 * Initialize the given application context.
	 * @param applicationContext the application to configure
	 */
	void initialize(C applicationContext);

}
ApplicationListener

SpringBoot3.X源码分析(启动流程),spring boot,java,开发语言,spring boot,源码

ApplicationEvent监听器:

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	void onApplicationEvent(E event);

	static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
		return event -> consumer.accept(event.getPayload());
	}

}
SpringApplicationEvent

SpringBoot3.X源码分析(启动流程),spring boot,java,开发语言,spring boot,源码

应用程序的:

  • 启动事件,ApplicationStartingEvent。一旦一个 SpringApplication 开始,事件就会尽早发布 - 在 or ApplicationContext 可用之前Environment,但在注册之后ApplicationListener。事件的来源是本身SpringApplication,但要注意不要在这个早期阶段过多地使用其内部状态,因为它可能会在生命周期的后期被修改
  • 失败事件,ApplicationFailedEvent
  • 准备事件,ApplicationPreparedEvent 事件发布为 当一个SpringApplication正在启动并且ApplicationContext已完全准备好但未refresh。将加载 Bean 定义,并在此阶段可以使用
  • ApplicationEnvironmentPreparedEvent
  • ContextClosedEvent

应用程序事件监听器跟监听事件是绑定的,如:

  • ConfigServerBootstrapApplicationListener只跟ApplicationEnvironmentPreparedEvent事件绑定
  • LiquibaseServiceLocatorApplicationListener只跟ApplicationStartedEvent事件绑定
  • LoggingApplicationListener跟所有事件绑定

3 SpringApplication的执行

先看一些事件和监听器概念:

  • SpringApplicationRunListeners类
  • SpringApplicationRunListener类
3.1 SpringApplicationRunListeners

用于监听SpringApplication的run方法的执行。用于SpringApplicationRunListener监听器的批量执行。

finish:run方法结束之前调用;对应事件的类型是ApplicationReadyEvent或ApplicationFailedEvent

class SpringApplicationRunListeners {

  // Log日志类
	private final Log log;

  // 持有SpringApplicationRunListener集合
	private final List<SpringApplicationRunListener> listeners;

	private final ApplicationStartup applicationStartup;

  // run方法执行时立马执行;对应事件类型ApplicationStartingEvent
	void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
		doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
				(step) -> {
					if (mainApplicationClass != null) {
						step.tag("mainApplicationClass", mainApplicationClass.getName());
					}
				});
	}

  // ApplicationContext创建之前并且环境信息准备好的时候调用;对应事件类型ApplicationEnvironmentPreparedEvent
	void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
		doWithListeners("spring.boot.application.environment-prepared",
				(listener) -> listener.environmentPrepared(bootstrapContext, environment));
	}

  // ApplicationContext创建好并且在source加载之前调用一次;没有具体的对应事件
	void contextPrepared(ConfigurableApplicationContext context) {
		doWithListeners("spring.boot.application.context-prepared", (listener) -> listener.contextPrepared(context));
	}

  // ApplicationContext创建并加载之后,并在refresh之前调用
  // 对应事件类型ApplicationPreparedEvent
	void contextLoaded(ConfigurableApplicationContext context) {
		doWithListeners("spring.boot.application.context-loaded", (listener) -> listener.contextLoaded(context));
	}


	void started(ConfigurableApplicationContext context, Duration timeTaken) {
		doWithListeners("spring.boot.application.started", (listener) -> listener.started(context, timeTaken));
	}

	void ready(ConfigurableApplicationContext context, Duration timeTaken) {
		doWithListeners("spring.boot.application.ready", (listener) -> listener.ready(context, timeTaken));
	}

	void failed(ConfigurableApplicationContext context, Throwable exception) {
		doWithListeners("spring.boot.application.failed",
				(listener) -> callFailedListener(listener, context, exception), (step) -> {
					step.tag("exception", exception.getClass().toString());
					step.tag("message", exception.getMessage());
				});
	}

	private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
			Throwable exception) {
		try {
			listener.failed(context, exception);
		}
		catch (Throwable ex) {
			if (exception == null) {
				ReflectionUtils.rethrowRuntimeException(ex);
			}
			if (this.log.isDebugEnabled()) {
				this.log.error("Error handling failed", ex);
			}
			else {
				String message = ex.getMessage();
				message = (message != null) ? message : "no error message";
				this.log.warn("Error handling failed (" + message + ")");
			}
		}
	}

	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
		doWithListeners(stepName, listenerAction, null);
	}

	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
			Consumer<StartupStep> stepAction) {
		StartupStep step = this.applicationStartup.start(stepName);
		this.listeners.forEach(listenerAction);
		if (stepAction != null) {
			stepAction.accept(step);
		}
		step.end();
	}

}

SpringApplicationRunListener目前只有一个实现类EventPublishingRunListener:

SpringBoot3.X源码分析(启动流程),spring boot,java,开发语言,spring boot,源码

/**
 * SpringApplicationRunListener to publish SpringApplicationEvents.
 * Uses an internal ApplicationEventMulticaster for the events that are fired
 * before the context is actually refreshed.
 */
class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	private final SimpleApplicationEventMulticaster initialMulticaster;

	@Override
	public void starting(ConfigurableBootstrapContext bootstrapContext) {
		multicastInitialEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
	}

	@Override
	public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
			ConfigurableEnvironment environment) {
		multicastInitialEvent(
				new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
	}

	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		multicastInitialEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
	}

	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware contextAware) {
				contextAware.setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
		multicastInitialEvent(new ApplicationPreparedEvent(this.application, this.args, context));
	}

	@Override
	public void started(ConfigurableApplicationContext context, Duration timeTaken) {
		context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context, timeTaken));
		AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
	}

	@Override
	public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
		context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context, timeTaken));
		AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
	}

	@Override
	public void failed(ConfigurableApplicationContext context, Throwable exception) {
		ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
		if (context != null && context.isActive()) {
			// Listeners have been registered to the application context so we should
			// use it at this point if we can
			context.publishEvent(event);
		}
		else {
			// An inactive context may not have a multicaster so we use our multicaster to
			// call all the context's listeners instead
			if (context instanceof AbstractApplicationContext abstractApplicationContext) {
				for (ApplicationListener<?> listener : abstractApplicationContext.getApplicationListeners()) {
					this.initialMulticaster.addApplicationListener(listener);
				}
			}
			this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
			this.initialMulticaster.multicastEvent(event);
		}
	}

  // 广播事件出去
	private void multicastInitialEvent(ApplicationEvent event) {
		refreshApplicationListeners();
		this.initialMulticaster.multicastEvent(event);
	}

	private void refreshApplicationListeners() {
		this.application.getListeners().forEach(this.initialMulticaster::addApplicationListener);
	}

	private static class LoggingErrorHandler implements ErrorHandler {

		private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class);

		@Override
		public void handleError(Throwable throwable) {
			logger.warn("Error calling ApplicationEventListener", throwable);
		}

	}

}

它把监听过程封装成SpringApplicationEvent事件,并让内部属性initialMulticaster,ApplicationEventMulticaster接口的实现类SimpleApplicationEventMulticaster广播出去:

SpringBoot3.X源码分析(启动流程),spring boot,java,开发语言,spring boot,源码

广播出去的事件对象会被SpringApplication中的listeners属性进行处理。

SpringBoot3.X源码分析(启动流程),spring boot,java,开发语言,spring boot,源码

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

SpringBoot3.X源码分析(启动流程),spring boot,java,开发语言,spring boot,源码

创建所有 Spring 运行监听器并发布应用启动事件

调用getSpringFactoriesInstances 获取配置的监听器名称,并实例化所有的类。

SpringApplicationRunListener所有监听器配置在 pring.factories :

SpringBoot3.X源码分析(启动流程),spring boot,java,开发语言,spring boot,源码文章来源地址https://www.toymoban.com/news/detail-817091.html

3.2 run
public ConfigurableApplicationContext run(String... args) {
    // 开始执行,记录开始时间
		long startTime = System.nanoTime();
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
    // 设置系统属性 java.awt.headless 的值,默认为true
		configureHeadlessProperty();
    
    // 获取SpringApplicationRunListeners,内部只有一个EventPublishingRunListener
    // 创建所有 Spring 运行监听器并发布应用启动事件
		SpringApplicationRunListeners listeners = getRunListeners(args);
    
    // 会封装成SpringApplicationEvent事件然后广播出去给SpringApplication中的listeners所监听
    // 这里接受ApplicationStartedEvent事件的listener会执行相应操作
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
      // 初始化默认应用参数类
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      
      // 根据运行监听器和应用参数来准备 Spring 环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			Banner printedBanner = printBanner(environment);
      
      // 创建Spring容器,即创建应用上下文
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
     // 准备应用上下文
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
      
      // 容器创建完成之后执行额外一些操作
			afterRefresh(context, applicationArguments);
			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}
      // 广播出ApplicationReadyEvent事件给相应的监听器执行
      // 发布应用上下文启动完成事件
			listeners.started(context, timeTakenToStartup);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			if (ex instanceof AbandonedRunException) {
				throw ex;
			}
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			if (context.isRunning()) {
				Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
        // 发布应用上下文就绪事件
				listeners.ready(context, timeTakenToReady);
			}
		}
		catch (Throwable ex) {
			if (ex instanceof AbandonedRunException) {
				throw ex;
			}
      // 过程报错的话会执行一些异常操作
      // 然后广播出ApplicationFailedEvent事件给相应的监听器执行
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
    // 返回Spring容器
		return context;
	}
设置系统属性 java.awt.headless 的值
private void configureHeadlessProperty() {
  System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
      System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

到了这里,关于SpringBoot3.X源码分析(启动流程)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot配置外部Tomcat项目启动流程源码分析

    SpringBoot应用默认以Jar包方式并且使用内置Servlet容器(默认Tomcat),该种方式虽然简单但是默认不支持JSP并且优化容器比较复杂。故而我们可以使用习惯的外置Tomcat方式并将项目打War包。 ① 同样使用Spring Initializer方式创建项目 ② 打包方式选择\\\"war\\\" ③ 选择添加的模块 ④ 创建的

    2024年02月04日
    浏览(41)
  • SpringBoot-Run启动流程(源码分析)—看不懂来揍我

    目录 前言 Run()方法 1、实例化SpringApplication对象 1、加载容器 2、装配初始化器 3、装配监听器  4、加载主类 2、执行Run()方法 1、设置headless 2、启用SpringApplicationListener 3、加载Banner 1、图片Banner 2、文本Banner 4、异常报告类加载 5、准备上下文         6、刷新上下文 7、系统

    2024年02月14日
    浏览(39)
  • 【SpringBoot3】Spring Boot 3.0 集成 Redis 缓存

    Redis缓存是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它主要用于作为数据库、缓存和消息中间件,以快速读写和丰富的数据结构支持而著称。 在应用程序和数据库之间,Redis缓存作为一个中间层起着关键

    2024年02月21日
    浏览(52)
  • 【SpringBoot3】Spring Boot 3.0 介绍以及新特性

    Spring Boot 3.0 是 Spring Boot 框架的一个重要版本,它在保持了 Spring Boot 的一贯优点的同时,也进行了一些重要的改进和更新。 首先,Spring Boot 3.0 对 Java 版本的要求进行了更新。这个版本要求使用 Java 17 作为最低版本,以利用最新的语言特性和性能改进。如果你正在使用的是

    2024年01月17日
    浏览(84)
  • Spring Boot 启动流程

    加载配置 Spring Boot在启动时会加载应用程序的配置文件(例如application.properties或application.yml),并将其转化为内部的配置对象。 创建应用程序上下文 Spring Boot会创建一个应用程序上下文(ApplicationContext),它是Spring框架的核心容器。应用程序上下文负责管理Bean的生命周期和

    2024年02月06日
    浏览(48)
  • Spring Boot的启动流程

    Spring Boot是作为Spring的脚手架框架,其本身并不提供Spring的核心功能,而是来达到快速构建项目、预置三方配置、开箱即用的目的 。 从本质上来说,Spring Boot就是Spring,它做了那些没有它你自己也会去做的Spring Bean配置。 Spring Boot使用“习惯优于配置”的理念让你的项目快速

    2024年02月02日
    浏览(37)
  • Spring Boot启动流程简析

    文章首发地址 可以穿件独立的Spring应用程序,可以创建可执行的jars 内嵌tomcat或jetty等Servlet容器 提供“入门”依赖项,以简化构建配置。尽可能自动配置Spring和第三方库 提供可用于生产的功能,例如指标、运行状况检查和外部化配置 了解基本的启动注解 AnnotationUtil.java,该

    2024年02月16日
    浏览(57)
  • springboot3.2报Spring Boot [3.2.0] is not compatible with this Spring Cloud release train

    这个解决也比较简单: 1、直接把springcloud升级为2023.0.0-RC1就行了 2、第二种,直接进行配置,关闭检查,但是微服务的相关服务可能无法使用,需要自行验证 spring.cloud.compatibility-verifier.enabled=false

    2024年02月20日
    浏览(48)
  • 三分钟了解Spring Boot 的启动流程

    👏作者简介:大家好,我是冰点,从业11年,目前在物流独角兽企业从事技术方面工作, 🍂博主正在努力完成2023计划中:以梦为马,扬帆起航,2023追梦人 📝联系方式:iceicepip,加我进群,大家一起学习,一起进步👀 背景:最近有位开发同学说面试被问到Spring Boot 的启动

    2024年02月08日
    浏览(46)
  • 全网最清楚的:Spring Boot 启动流程讲解

    Spring Boot 启动流程 简介 步骤 加载配置 创建应用程序上下文 执行自动配置 启动应用程序 处理请求 源码层说明 扩展 自定义注解以及自定义注解实现中有bean,与启动流程什么有关 Bean扫描 注解处理 Spring Boot 的启动流程 充分利用了 Spring 框架的强大功能,同时又为开发者提供

    2024年02月07日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包