欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot启动流程的简析

程序员文章站 2024-03-26 13:50:29
...

SpringBoot启动流程的简析

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

调试代码

我们在SpringBoot的主启动类里面run方法上开启断点
SpringBoot启动流程的简析
然后开始进入里面的方法,看看到底经历了些什么?

然后我们向下走进入到run方法的核心,来看一看梦开始的地方:
SpringBoot启动流程的简析
一、首先开启时间监听

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
SpringBoot启动流程的简析
走到第二步配置环境
SpringBoot启动流程的简析
SpringBoot启动流程的简析
四、创建应用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的实例化等核心工作。

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

相关标签: SpringBoot java