Spring Boot执行流程

Spring Boot的准备

首先,我们看一下在启动项中 SpringApplication为run的启动做了哪些准备:

SpringApplication方法中调用了其它的方法

我们首先来看一看 this.webApplicationType = WebApplicationType.deduceFromClasspath();

public enum WebApplicationType {

	/**
	 * The application should not run as a web application and should not start an
	 * embedded web server.
	 */
	NONE,

	/**
	 * The application should run as a servlet-based web application and should start an
	 * embedded servlet web server.
	 */
	SERVLET,

	/**
	 * The application should run as a reactive web application and should start an
	 * embedded reactive web server.
	 */
	REACTIVE;

	private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };

	private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";

	private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

	private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

	private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";

	private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";

	static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

	static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
		if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
			return WebApplicationType.SERVLET;
		}
		if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
			return WebApplicationType.REACTIVE;
		}
		return WebApplicationType.NONE;
	}

	private static boolean isAssignable(String target, Class<?> type) {
		try {
			return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
		}
		catch (Throwable ex) {
			return false;
		}
	}

}

 上面的便是WebApplicationType的源码,可以看出调用的deduceFromClasspath()方法的内容。

大体来说的意思就是:

如果存在 org.springframework.web.reactive.DispatcherHandler 类

并且不存在org.springframework.web.servlet.DispatcherServlet类和org.glassfish.jersey.servlet.ServletContainer类时,则决定使用 REACTIVE 模式;

如果javax.servlet.Servlet类和org.springframework.web.context.ConfigurableWebApplicationContext类有任意一个不存在,则决定使用NONE模式;

否则的话就采用SERVLET模式。

由于spring-boot-start-web依赖引入了tomcat和spring-web,所以会自动选择SERVLET模式。

接着,通过getSpringFactoriesInstances()方法来加载ApplicationContextInitializer

和加载ApplicationListener

我们来看一看getSpringFactoriesInstances()方法

 在此方法中又使用SpringFactoriesLoader类来将类路径下的所有resources/META-INF/spring.factory文件的key作为BootStrapper接口的值都加载出来作为该接口的实现类列表。

接着调用createSpringFactoriesInstances()方法

 此方法通过实现类来实现,通过反射完成。

搞清楚getSpringFactoriesInstances()方法之后,那么对于

加载ApplicationContextInitializer,便就是获取ApplicationContextInitializer接口实现类并创建实例对象,赋值给initializers成员变量;

加载ApplicationListener,便就是获取ApplicationListener接口实现类并创建实例对象,赋值给listeners成员变量。

最后调用deduceMainApplicationClass()

 通过手动创建一个RuntimeException对象,并获取当前方法的堆栈,一直向上,直到推到main方法所在类并返回。

Spring Boot运行阶段

	/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			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;
	}

我们来看一下run()方法的源码

StopWatch是一个小组件,这里是计算运行时间的一个小工具

首先调用configureHeadlessProperty()方法设置headless模式

通过配置java.awt.headless属性(这里默认为true)为true

接着获取所有的监听器

 从spring.facotries内找到所有SpringApplicationRunListener接口的实现来创建实例对象,将这些对象封装到SpringApplicationRunListeners对象中;

发送Spring Boot开始事件

 环境配置

首先将run()方法中的参数封装成ApplicationArguments;

再进行初始化和配置Environment对象;

最后设置IngoreBeanInfoProperty的属性,决定是否跳过spring对BeanInfo类的扫描。

打印banner

首先判断当前banner模式是否开启

如果是Log模式,则使用logger进行打印banner

否则采用标准输出流输出banner

如果是logger模式则会将banner转成string,在用logger的info级别进行输出

如果是输出流模式,则直接调用banner的printBanner方法进行输出

创建ApplicationContext

上面我们说过,webApplicationType的模式为SERVLET,所以我们创建的ApplicationContext对象就是AnnotationConfigServletWebServerApplicationContext 

初始化配置ApplicationContext

refreshContext

 AfterRefresh

 context启动事件 & Runner调用 & 运行

 1.调用所有SpringApplicationRunListener的started()方法

 2.回调ApplicationRunner和CommandLineRunner

3.调用所有SpringApplicationRunListener的running()方法

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值