SpringBoot启动流程的简析

SpringBoot启动流程的简析

第一次系统的走读SpringBoot的启动流程,可能讲述的不准确!有些东西也是参考网上的其他教程, 进行总结,所以不正确的地方各位大佬可以留言!

调试代码

我们在SpringBoot的主启动类里面run方法上开启断点
在这里插入图片描述
然后开始进入里面的方法,看看到底经历了些什么?

然后我们向下走进入到run方法的核心,来看一看梦开始的地方:
在这里插入图片描述
一、首先开启时间监听

StopWatch stopWatch = new StopWatch();
stopWatch.start();

简单的定时任务器,就是来统计SpringBoot加载时间的,每次启动在最后都会看到启动时长就是他展示的!

二、开启运行监听器

SpringApplicationRunListeners listeners = getRunListeners(args);

进去看下源码:

private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

上面代码通过SpringFactoriesLoader检索META-INF/spring.factories找到声明的所有SpringApplicationRunListener的实现类并将其实例化,然后装配到List运行监听器集合中。

listeners.started();用于遍历运行监听器集合中的所有SpringApplicationRunListener的实现类,并逐一调用它们的starting方法,广播Spring Boot应用要开始启动了。

三、创建环境

ConfigurableEnvironment environment = prepareEnvironment(listeners,
		applicationArguments);

源码:

	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		//判断环境是否是空,不是就返回当前对象。这里返回的是StandardServletEnvironment 见下图1-1说明
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//配置环境
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

图:1-1
当前的webApplicationType的类型是SERVLET
在这里插入图片描述
走到第二步配置环境
在这里插入图片描述
在这里插入图片描述
四、创建应用context

context = createApplicationContext();

源码:

	/**
	 * The class name of application context that will be used by default for web
	 * environments.
	 */
	public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
			+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";


/**
	 * Strategy method used to create the {@link ApplicationContext}. By default this
	 * method will respect any explicitly set application context or application context
	 * class before falling back to a suitable default.
	 * @return the application context (not yet refreshed)
	 * @see #setApplicationContextClass(Class)
	 */
	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, "
								+ "please specify an ApplicationContextClass",
						ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

五、getSpringFactoriesInstances

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

当前对象返回一个实例

六、装配Context

prepareContext(context, environment, listeners, applicationArguments, printedBanner);

源码:

	private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		applyInitializers(context);
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot specific singleton beans
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	}

prepareContext方法开头为ApplicationContext加载了environment,之后通过applyInitializers方法逐个执行ApplicationContextInitializer的initialize方法来进一步封装ApplicationContext,并调用所有的SpringApplicationRunListener实现类的contextPrepared方法,广播ApplicationContext已经准备完毕了。

七、刷新完成之后监听器启动上下文

refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			//started方法会调用所有的SpringApplicationRunListener的finished方法,广播SpringBoot应用已经成功启动。
			listeners.started(context);

八、执行完毕
run方法中的这行代码callRunners(context, applicationArguments);遍历所有ApplicationRunner和CommandLineRunner的实现类,并执行其run方法。我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对Spring Boot的启动过程进行扩展。

总结一下:
在springboot运行阶段的时候,主要经过了如下几个关键步骤

  • 开启spring应用执行监听器,对应上述步骤第二步
  • 加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment,对应上述步骤第三步
  • 配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners),对应上述步骤第三步
  • 创建应用上下文,对应上述步骤第四步
  • prepareContext方法将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联,对应上述步骤第六步
  • 接下来的refreshContext(context)方法(初始化方法如下)将是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载,bean的实例化等核心工作。

上述代码仅仅只是粗略的过了大概,我总共才调试了半个小时,要是想完完整整的理解他,还需要更多的时间和耐心,一边调通看懂,几乎没人可以做到!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值