springBoot 源码四:springboot启动流程源码分析

springboot启动流程源码分析

之前分析了springboot启动类@SpringBootApplication注解的部分功能。那么接下来分析springboot的启动流程和@SpringBootApplication剩下的功能。
我们来看下springboot启动类有哪些代码

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

MyApplication.class等价于spring的配置类,启动的时候会解析这个类。
args表示运行时指定的参数,就是 java -jar xxx.jar --xxx=xxx
知道了这两点后,来看下run方法做了什么。

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

其中new SpringApplication(primarySources)创建SpringApplication对象,run(args)就是具体的运行逻辑
先来看下new SpringApplication(primarySources)做了什么

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		//把启动类传入的配置类也就是MyApplication  放入到primarySources 属性当中
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 1. 推测web应用类型(NONE、REACTIVE、SERVLET)
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 2. 从spring.factories中获取BootstrapRegistryInitializer对象
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		// 3. 从spring.factories中获取ApplicationContextInitializer对象
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// 4. 从spring.factories中获取ApplicationListener对象
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 5. 推测出Main类(main()方法所在的类)
		this.mainApplicationClass = deduceMainApplicationClass();
	}

根据上面源码,构建SpringApplication对象时主要经历一下步骤:

  1. 推测web应用类型: 主要是通过判断哪些类有,哪些类没有来推断,这里不展开。把推断的结果保存到webApplicationType属性中
  2. 从spring.factories中获取BootstrapRegistryInitializer对象 通常情况下为空
  3. 从spring.factories中获取ApplicationContextInitializer对象
    在这里插入图片描述
  4. 从spring.factories中获取ApplicationListener对象
    在这里插入图片描述
  5. 推测出Main类(main()方法所在的类)

构建完SpringApplication对象后执行run方法启动springboot容器。这才是重点

public ConfigurableApplicationContext run(String... args) {
		long startTime = System.nanoTime();
		// 1、创建引导启动器,类似一个ApplicationContext,可以往里面添加一些对象
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		//关于awt的
		configureHeadlessProperty();
		// 2、从spring.factories中获取SpringApplicationRunListener对象
		// 默认会拿到一个EventPublishingRunListener,它会启动过程的各个阶段发布对应的ApplicationEvent事件
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 3、发布ApplicationStartingEvent
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			// 4、将run()的参数封装为DefaultApplicationArguments对象
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 5、准备Environment
			// 包括操作系统,JVM、ServletContext、properties、yaml等等配置
			// 会发布一个ApplicationEnvironmentPreparedEvent
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			// 默认spring.beaninfo.ignore=true,表示不需要jdk缓存beanInfo信息,Spring自己会缓存
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			// 6、根据应用类型创建Spring容器
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
			// 7、利用ApplicationContextInitializer初始化Spring容器
			// 8、发布ApplicationContextInitializedEvent
			// 9、关闭DefaultBootstrapContext
			// 10、注册primarySources类,就是run方法存入进来的配置类
			// 11、发布ApplicationPreparedEvent事件
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
			// 12、刷新Spring容器,会解析配置类、扫描、启动WebServer
			refreshContext(context);
			// 空方法
			afterRefresh(context, applicationArguments);
			// 启动时间
			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}
			// 13、发布ApplicationStartedEvent事件,表示Spring容器已经启动
			listeners.started(context, timeTakenToStartup);
			// 14、从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			// 15、发布ApplicationFailedEvent事件
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
			// 16、发布ApplicationReadyEvent事件,表示Spring容器已经准备好了
			listeners.ready(context, timeTakenToReady);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

大致流程是
1. 创建引导启动器,类似一个ApplicationContext
DefaultBootstrapContext bootstrapContext = createBootstrapContext();

private DefaultBootstrapContext createBootstrapContext() {
		DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
		// 利用bootstrapRegistryInitializers初始化DefaultBootstrapContext
		this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
		return bootstrapContext;
	}

bootstrapRegistryInitializers就是之前构建SpringApplication获取到的,一般为空
2. 拿到事件发布对象
SpringApplicationRunListeners listeners = getRunListeners(args);
默认情况下SpringBoot提供了一个EventPublishingRunListener,它实现了SpringApplicationRunListener接口,默认情况下会利用EventPublishingRunListener发布一个ApplicationContextInitializedEvent事件,程序员可以通过定义ApplicationListener来消费这个事件
在这里插入图片描述
EventPublishingRunListener有很多方法,每个方法表示一个事件发布的时间节点,如下图
在这里插入图片描述

starting : springboot开始启动事件
environmentPrepared:环境变量准备完成事件
contextPrepared:spring容器准备事件
contextLoaded:spring容器准备完成事件
started:spring容器启动事件
ready:spring容器启动成功事件
failed:spring容器启动失败事件

3.发布事件ApplicationStartingEvent
listeners.starting(bootstrapContext, this.mainApplicationClass);
发布springboot开始启动事件,bootstrapContext就是第一步创建的对象, this.mainApplicationClass就是推断出main方法的主类

4、将run()的参数封装为DefaultApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

public class DefaultApplicationArguments implements ApplicationArguments {
	private final Source source;
	private final String[] args;
public DefaultApplicationArguments(String... args) {
		Assert.notNull(args, "Args must not be null");
		this.source = new Source(args);
		this.args = args;
	}
}	

5.准备环境变量
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
把系统参数、jvm参数、配置文件的参数封装起来。同时发布环境准备完成事件
这部分和springboot配置优先级有关 下一篇在具体分析
6、根据应用类型创建Spring容器
context = createApplicationContext();

protected ConfigurableApplicationContext createApplicationContext() {
		return this.applicationContextFactory.create(this.webApplicationType);
	}

ApplicationContextFactory DEFAULT = (webApplicationType) -> {
		try {
			switch (webApplicationType) {
			case SERVLET:
				return new AnnotationConfigServletWebServerApplicationContext();
			case REACTIVE:
				return new AnnotationConfigReactiveWebServerApplicationContext();
			default:
				return new AnnotationConfigApplicationContext();
			}
		}
		catch (Exception ex) {
			throw new IllegalStateException("Unable create a default ApplicationContext instance, "
					+ "you may need a custom ApplicationContextFactory", ex);
		}
	};

this.webApplicationType 就是开头推断web应用类型的值
应用类型为 SERVLET,则对应AnnotationConfigServletWebServerApplicationContext
应用类型为REACTIVE,则对应AnnotationConfigReactiveWebServerApplicationContext
应用类型为普通类型,则对应AnnotationConfigApplicationContext

7 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
这里面涉及到内容挺多,但是不复杂

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		// 将前面生成的Environment设置到Spring容器中
		context.setEnvironment(environment);
		// 将设置在SpringApplication上的beanNameGenerator、resourceLoader设置到Spring容器中
		postProcessApplicationContext(context);
		// 利用ApplicationContextInitializer初始化Spring容器
		applyInitializers(context);
		// 发布ApplicationContextInitializedEvent事件,表示Spring容器初始化完成
		listeners.contextPrepared(context);
		// Spring容器初始化好了,就关闭DefaultBootstrapContext
		bootstrapContext.close(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// 注册一些单例Bean
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}

		// 设置allowCircularReferences和allowBeanDefinitionOverriding给Spring容器
		if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
			((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
			if (beanFactory instanceof DefaultListableBeanFactory) {
				((DefaultListableBeanFactory) beanFactory)
						.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
			}
		}

		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}

		// Load the sources
		// 拿到启动配置类(run方法传进来的类)
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");

		// 将启动配置类解析为BeanDefinition注册到Spring容器中
		load(context, sources.toArray(new Object[0]));

		// 发布ApplicationPreparedEvent事件,表示Spring容器已经准备好
		listeners.contextLoaded(context);
	}

这一步会发送两个事件,最终会把spring容器准备完成。注意这个阶段只是spring容器准备好,并没有进行扫描和解析,也就是说此时的spring容器中没有我们定义的bean。

8 刷新Spring容器,会解析配置类、扫描、启动WebServer
spring容器准备完成后就到 refreshContext(context);这一步就开始解析配置类,也就是解析@SpringBootApplication注解,扫描我们定义bean,解析扫描到的bean,加载到spring容器中。这些步骤就和spring启动流程是一样,spring怎么扫描的,怎么解析的,这里就是一样的。
refreshContext(context);调用的核心方法

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
				contextRefresh.end();
			}
		}
	}

这个refresh()代码就是spring启动流程中的refresh()。这部分代码在spring启动过程源码分析中分析过,这里不再赘述。
springboot的refresh()方法和spring的refresh()不同的地方在于onRefresh();方法。spring的onRefresh();方法是一个空方法,而springboot的onRefresh();方法涉及到web容器的启动。这部分在springboot整合tomcat的分析中分析过,这里也不再重复。

9 发布spring容器启动事件
listeners.started(context, timeTakenToStartup);

10调用ApplicationRunner和CommandLineRunner
callRunners(context, applicationArguments);
context就是spring容器
applicationArguments 是在第四步中,将run方法的参数封装为applicationArguments
方法流程大概是

  • 获取Spring容器中的ApplicationRunner类型的Bean
  • 获取Spring容器中的CommandLineRunner类型的Bean
  • 执行它们的run()

具体是用来干嘛的呢,下一篇在举个例子分析。

11 发布失败事件
handleRunFailure(context, ex, listeners);
如果springboot启动过程发生异常,发布启动失败的事件

12 发布启动成功事件
listeners.ready(context, timeTakenToReady);
表示springboot启动成功
到此springboot的启动流程就结束了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值