SpringBoot启动流程

SpringBoot的启动都需要如下的启动类

java 复制代码
@SpringBootApplication

public class SpringBootDemoApplication {

	public static void main(String[] args) {

		SpringApplication.run(SpringBootDemo3Application.class, args);

	}

}

分析启动类, 可以看出核心是:

  • 注解@SpringBootApplication
  • 方法SpringApplication.run(SpringBootDemoApplication.class, args)
    其中@SpringBootApplication之前已经了解过,如果想要了解可以查看SpringBoot自动装配原理及分析

一、方法SpringApplication.run(SpringBootDemoApplication.class, args)

java 复制代码
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {

	return run(new Class<?>[] { primarySource }, args);

}

 

//继续点进下一层run方法

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {

	return new SpringApplication(primarySources).run(args);

}
  • 首先new了一个SpringApplication对象

  • 然后调用SpringApplication对象的run方法

二、实例化SpringApplication对象

调用SpringApplication的1个参数的构造函数,将启动类信息传入

java 复制代码
public SpringApplication(Class<?>... primarySources) {
   this(null, primarySources);
}

继续调用2个参数的构造方法,传入2个参数

java 复制代码
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.factories寻找
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //获取所有的应用监听器, 从spring.factories寻找
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	//推断main方法所在类
	this.mainApplicationClass = deduceMainApplicationClass();
}
  • 当前web应用类型
  • 所有的初始化器
  • 所有的应用监听器
  • 在项目依赖所有的spring.factories文件中可以找到以上初始化器和监听器
java 复制代码
#路径springframework\boot\spring-boot\2.3.4.RELEASE\spring-boot-2.3.4.RELEASE.jar!\META-INF\spring.factories
 
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
 
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
 
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
 
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
 
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
 
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
 
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.context.properties.NotConstructorBoundInjectionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
 
# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

源码解析

Springboot启动时,第一件重要事件就是构造SpringApplication对象,并主要完成如下事情。

  • 设置源。实际就是设置Spring容器启动时依赖的初始配置类,也就是Springboot中的启动类;
  • 设置WEB应用程序类型。例如可以是SERVLET,REACTIVE等;
  • 加载并设置Bootstrapper,ApplicationContextInitializer和ApplicationListener;
  • 设置应用程序主类的Class对象
    然后调用run(),具体步骤如下:

三、调用SpringApplication对象的run方法

将main方法中的args参数传入, 分析源码

java 复制代码
public ConfigurableApplicationContext run(String... args) {
	// 秒表,用于记录启动时间;记录每个任务的时间,最后会输出每个任务的总费时
	StopWatch stopWatch = new StopWatch();
    //记录启动时间
	stopWatch.start();
	ConfigurableApplicationContext context = null;
	Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    //让当前应用进入headless模式(自力更生模式)。java.awt.headless
	configureHeadlessProperty();
    //获取所有的运行监听器(这个监听器和普通的Spring监听器不同,它只用于Spring容器的启动过程中)
    //默认会拿到一个EventPublishiingRunListener,它会在启动过程中的各个阶段发布对应的ApplicationEvent事件
	SpringApplicationRunListeners listeners = getRunListeners(args);
    //遍历 SpringApplicationRunListener 调用 starting 方法;相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting
	listeners.starting();
	try {
    	//保存命令行参数(args),将启动参数args封装为ApplicationArguments对象
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //准备环境, 返回或者创建基础环境信息对象 : StandardServletEnvironment
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        //根据环境配置需要忽略的bean信息
		configureIgnoreBeanInfo(environment);
       	//打印banner
        Banner printedBanner = printBanner(environment);
        //创建IOC容器(createApplicationContext())
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
			new Class[] { ConfigurableApplicationContext.class }, context);
        //准备IOC容器的基本信息
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //刷新IOC容器。refreshContext --->  创建容器中的所有组件(Spring注解)
        refreshContext(context);
        //容器刷新完成后 ---> 这是一个空方法,(目前无内容, 预留)
        afterRefresh(context, applicationArguments);
        //停止运行监听器停止运行, 记录容器启动完成花费的时间
        stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
        //所有监听器调用 listeners.started(context); 通知所有的监听器容器已创建完成
		listeners.started(context);
        //调用所有runners, 遍历执行runner的run方法
		callRunners(context, applicationArguments);
	}catch (Throwable ex) {
	//IOC容器创建失败,监听器会发布一个创建失败的事件
		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保存一些信息,应用名字,当前启动时间

2、创建引导上下文(context)

2.1:获取到所有之前创建的bootstrapers,挨个执行initialize方法,来完成对引导启动器上下文环境设置。(即引导启动的容器,在创建IOC容器前需要用到的公共对象就放在该容器中)

3、让当前应用进入headless模式。(用于在缺失显示屏、鼠标、键盘时候的系统配置)

4、获取所有RunListener运行时监听器(为了方便所有Listener进行事件感知,项目在启动)

4.1.getSpringFactoriesInstances去所有依赖的Spring.factories找SpringApplicationRunListener

4.2遍历所有SpringApplicationRunListener,调用starting方法,监听所有项目运行的状态(通知所有感兴趣系统正在启动过程的代码,项目正在starting)

4.3保存命令行参数;

4.4准备环境prepareEnviroment

4.4.1返回创建基础环境信息。StandardServletEnviroment

4.4.2配置环境信息对象(properties等)

4.4.2.1 读取所有配置元的配置属性

4.4.3绑定环境信息

4.4.4监听器调用environmentPrepared;通知所有监听器,当前环境准备完成。

4.5创建IoC容器(createApplicationContext)

4.5.1根据项目类型(Servlet)创建容器

4.5.2当前会创建AnnotationConfigServletWebServerApplicationContext;

4.6准备ApplicationContext IOC容器基本信息 prepareContext方法

4.6.1 保存环境信息

4.6.2 IOC容器的后置处理流程

4.6.3 应用初始化;applyInitializers

4.6.3.1 遍历所有的ApplicationContxtInitializer,调用initialize,来对IOC容器进行初始化扩展

4.7.4 遍历所有的Listener调用ContextPrepared。EventPublishListener;通知所有的监听器ContextPrepared

4.7.5 所有监听器 调用contextLoaded,通知上下文IOC容器加载完成。

4.7刷新IOC容器refresh(核心源码)

4.7.1创建容器中的所有组件

4.8刷新容器后的工作(空方法,扩展点,模板方法)

4.8.1所有监听器调用started方法,当前项目启动了

4.9调用runner方法;callRunners()

4.9.1获取容器中的ApplicationRunner

4.9.2获取容器中CommandLineRunner

4.9.3合并所有runner,并且按照@order进行排序

4.9.4遍历所有的runner调用run方法。

4.10如果有异常会调用Listener的failed方法

4.11调用所有监听器running方法Listener.running方法

4.12running方法如果有异常,还是调用Listener的failed方法。

四、总结

启动流程

  1. 从main方法开始。首先运行静态的run方法,创建一个springApplication对象,再运行run方法,工厂初始化配置在构造函数中完成。

    构造函数中进行了初始化配置:通过类加载器,(loadFactories)读取classpath下所有的spring.factories配置文件,创建一些初始化配置对象;

    run方法:通知监听器应用程序启动开始,创建环境对象environment,用于读取环境配置,如application.yml.

  2. 创建应用程序上下文-createApplicationContext,创建bean工厂对象

  3. 刷新上下文(启动核心)refreshContxt(工厂对象配置、bean处理器配置、类的扫描、解析、bean定义、bean类信息缓存、服务器Tomcat创建、bean实例化、动态代理对象创建)。

    3.1 配置工厂对象,包括上下文类加载器,对象发布处理器,beanFactoryPostProcessor。

    3.2 注册并实例化bean工厂发布处理器,并且调用这些处理器,对包扫描(主要是class)文件。

    3.3 注册并实例化bean发布处理器beanPostProcessor。

    3.4 初始化一些与上下文有特别关系的bean对象(创建tomcat服务器)

    3.5 实例化所有bean工 程缓存的bean对象

    3.6 发布通知上下文刷新完成(启动tomcat)

  4. 通知监听者-启动程序完成

    启动中大部分对象都是beanFactory对象通过反射创建

相关知识点:

1.Spring监听器能帮助开发者监听web中特定的事件,比如比如 ServletContext, HttpSession, ServletRequest 的创建和销毁;变量的创建、销毁等等。

它采用了观察者模式,观察者模式的模型主要是由 观察者实体 和 主题实体 构成。

而Spring的监听器模式则结合了Spring本身的特征,也就是容器化。在Spring中,监听器

实体全部放在ApplicationContext中,事件也是通过ApplicationContext来进行发布,具体模型如下:
我们不难看到,虽说是通过ApplicationContext发布的事件,但其并不是自己进行事件的发布,而是引入了一个处理器------ EventMulticaster,直译就是事件多播器,它负责在大量的监听器中,针对每一个要广播的事件,找到事件对应的监听器,然后调用该监听器的响应方法,图中就是调用了监听器1、3、6。

PS: 只有在某类事件第一次广播时,EventMulticaster才会去做遍历所有监听器的事,当它针对该类事件广播过一次后,就会把对应监听器保存起来了,最后会形成一个缓存Map,下一次就能直接找到这些监听器

java 复制代码
final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);

Spring监听器中不仅采用了观察者模式,但其实它还用到了适配器模式,工厂模式等。

想要了解具体原理分析参考https://blog.csdn.net/u011709538/article/details/130833755

相关推荐
rzl023 分钟前
java web5(黑马)
java·开发语言·前端
君爱学习9 分钟前
RocketMQ延迟消息是如何实现的?
后端
guojl23 分钟前
深度解读jdk8 HashMap设计与源码
java
Falling4227 分钟前
使用 CNB 构建并部署maven项目
后端
guojl28 分钟前
深度解读jdk8 ConcurrentHashMap设计与源码
java
程序员小假37 分钟前
我们来讲一讲 ConcurrentHashMap
后端
爱上语文1 小时前
Redis基础(5):Redis的Java客户端
java·开发语言·数据库·redis·后端
A~taoker1 小时前
taoker的项目维护(ng服务器)
java·开发语言
萧曵 丶1 小时前
Rust 中的返回类型
开发语言·后端·rust