Spring Boot 启动深究SpringApplication执行流程

Spring Boot启动初探的文章中,我们了解到@SpringBootApplication注解起到的作用。接下来我们探究一下SpringApplication.run()方法,在应用启动的背后都做了哪些事情。让我对Spring Boot的了解更深一层。

演示环境

  • IntelliJ IDEA 2018.2.5 (Ultimate Edition)
  • JDK 1.8
  • Spring Boot 2.1.1.RELEASE
  • Maven 3.5.4

初始化Spring Boot项目

初始化项目我们就不多阐述了,大家自行解决吧。当我们初始完项目后都会有一个类似的启动类,代码如下:

@SpringBootApplication
public class JeromeApplication {

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

启动项目就是运行这个main方法就行了,接下来我们就以SpringApplication.run(JeromeApplication.class, args);作为入口,来探究一下项目启动的时候Spring Boot到底做了些什么。

进入源码部分

当我们使用工具跟run方法的时候,会走到它一个重载方法在1258行:

	// 代码跟到这儿,会调用run方法的重载方法
	public static ConfigurableApplicationContext run(Class<?> primarySource,
			String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}	
	
	// 实际运行这个方法
	public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例的run方法。

探究SpringApplication的构造阶段

当我们使用默认的方式启动项目,我们可以根据上面那段代码看出通过new SpringApplication(primarySources)创建了一个对象,我们进入到这个构造方法然后再跳到重载方法(263行):

	// 同样会运行重载方法
	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}	

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 1、进行Web应用的类型推断
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 2、加载应用上下文初始化器
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
        // 3、加载应用事件监听器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 4、推断引导类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

根据上面的代码我们来说一下构造阶段主要具体都干了写什么。

1、进行Web应用的类型推断

根据WebApplicationType这个类的静态方法deduceFromClasspath()推断Web应用类型,其实也就是根据当前的应用 ClassPath中是否存在相关实现类来推断 Web 应用的类型,类型有三种:

  • Web Reactive: WebApplicationType.REACTIVE
  • Web Servlet: WebApplicationType.SERVLET
  • 非 Web: WebApplicationType.NONE
	// 类的全路径的静态常量
	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";

	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;
	}

根据上面的代码可以看出推断的逻辑:

  • WEBFLUX_INDICATOR_CLASS存在并且WEBMVC_INDICATOR_CLASSJERSEY_INDICATOR_CLASS都不存在的时候,Web的类型才会被推断成Web Reactive。
  • WEBFLUX_INDICATOR_CLASSSERVLET_INDICATOR_CLASSES都不存在的时候,Web的类型才会被推断成非 Web类型。
  • 其它情况都会被推断成Web Servlet的类型,所以当Reactive和Servlet同时存在的时候,Reactive会被覆盖掉。

2、加载应用上下文初始化器

利用 Spring 工厂加载机制,实例化 ApplicationContextInitializer实现类,并排序对象集合。

	// 在这里传入的是ApplicationContextInitializer.class
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// 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;
	}

这里利用SpringFactoriesLoader来加载spring-boot-autoconfigure-2.1.1.RELEASE.jar包里面的META-INF/spring.factories配置文件,来读取key为ApplicationContextInitializer.class的全路径名org.springframework.context.ApplicationContextInitializer配置的值,然后再根据这些names去实例化这些对象,最后在根据Order进行排序。实现Order的两种方式:

  • 实现org.springframework.core.Ordered接口,重写int getOrder();方法。
  • 使用@Order注解进行标注。

3、加载应用事件监听器

利用 Spring 工厂加载机制,实例化 ApplicationListener实现类,并排序对象集合。其实这个和上下文的初始化器的加载时同样的原理。

    // 在这里传入的是ApplicationListener.class
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return getSpringFactoriesInstances(type, new Class<?>[] {});
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        // 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;
    }

同样是利用SpringFactoriesLoader来加载spring-boot-autoconfigure-2.1.1.RELEASE.jar包里面的META-INF/spring.factories配置文件,来读取key为ApplicationListener.class的全路径名org.springframework.context.ApplicationListener配置的值,然后再根据这些names去实例化这些对象,最后在根据Order进行排序。

4、推断引导类

利用异常的堆栈信息来推断引导类,也就是我们启动Spring Boot项目的那个main方法所在的类。

    private Class<?> deduceMainApplicationClass() {
       try {
          StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
          for (StackTraceElement stackTraceElement : stackTrace) {
             if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
             }
          }
       }
       catch (ClassNotFoundException ex) {
          // Swallow and continue
       }
       return null;
    }

通过异常的堆栈信息中方法名为main的栈帧来得到入口类的名字。

到这里SpringApplication就已经完成了在构造阶段所初始化的过程。

探究SpringApplication的运行阶段

当我们执行完new SpringApplication(primarySources)后,就会调用该对象的run方法。

	public ConfigurableApplicationContext run(String... args) {
        // 启动一个计时器
		StopWatch stopWatch = new StopWatch();
        // 开始计时
		stopWatch.start();
        // 定义局部变量
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        // 设置java.awt.headless系统属性为true - 没有图形化界面
		configureHeadlessProperty();
        // 1、加载SpringApplication运行监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
        // 2、运行SpringApplication运行监听器,Spring应用刚启动
		listeners.starting();
		try {
            // 根据传入的args,创建ApplicationArguments对象
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
            // 4、创建 Environment,这里调用listeners.environmentPrepared(ConfigurableEnvironment)
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
            // 打印Banner
			Banner printedBanner = printBanner(environment);
            // 5、创建Spring应用上下文
			context = createApplicationContext();
            // 异常分析报告的记录容器的初始化
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
            // Spring上下文前置处理,这里调用listeners.contextPrepared(context)和listeners.contextLoaded(context)
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
            // Spring上下文刷新
			refreshContext(context);
            // Spring上下文后置处理
			afterRefresh(context, applicationArguments);
            // 停止计时
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
            // Spring 应用已经启动
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
            // 处理异常,并会打印异常分析报告,并调用listeners.failed(context, exception);
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
            // Spring 应用正在运行
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

1、加载 SpringApplication运行监听器

这个加载运行监听器的方式,大家仔细一看就会发现,和初始化阶段加载应用上下文初始化器和事件监听器的方法如出一辙。

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

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// 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;
	}

通过上面的代码也可以看出,同样是利用的工厂加载机制读取SpringApplicationRunListener.class的全路径名org.springframework.boot.SpringApplicationRunListener配置的值,然后再根据这些names去实例化这些对象,最后在根据Order进行排序。获取这些对象的集合后会被封装到SpringApplicationRunListeners这个类里面。

2、运行 SpringApplication运行监听器

在这里SpringApplicationRunListener监听了多个运行状态方法,参照下表:

监听方法阶段说明Spring Boot 起始版本
starting()Spring 应用刚启动1.0
environmentPrepared(ConfigurableEnvironment)ConfigurableEnvironment 准备妥当,允许将其调整1.0
contextPrepared(ConfigurableApplicationContext)ConfigurableApplicationContext 准备妥当,允许将其调整1.0
contextLoaded(ConfigurableApplicationContext)ConfigurableApplicationContext 已装载,但仍未启动1.0
started(ConfigurableApplicationContext)ConfigurableApplicationContext 已启动,此时 Spring Bean 已初始化完成2.0
running(ConfigurableApplicationContext)Spring 应用正在运行2.0
failed(ConfigurableApplicationContext,Throwable)Spring 应用运行失败2.0

3、监听Spring Boot事件

我们在加载阶段,通过工厂机制加载了 SpringApplicationRunListener的实现类 EventPublishingRunListener,这个类利用SimpleApplicationEventMulticaster来广播 Spring Boot 事件。

EventPublishingRunListener监听方法与 Spring Boot事件对照表:

监听方法Spring Boot 事件Spring Boot 起始版本
starting()ApplicationStartingEvent1.5.0
environmentPrepared(ConfigurableEnvironment)ApplicationEnvironmentPreparedEvent1.0
contextPrepared(ConfigurableApplicationContext)ApplicationContextInitializedEvent2.1.0
contextLoaded(ConfigurableApplicationContext)ApplicationPreparedEvent1.0
started(ConfigurableApplicationContext)ApplicationStartedEvent2.0.0
running(ConfigurableApplicationContext)ApplicationReadyEvent1.3.0
failed(ConfigurableApplicationContext,Throwable)ApplicationFailedEvent1.0

4、创建 Environment

根据构造阶段的推断出来的Web应用类型创建对应的 ConfigurableEnvironment实例:

	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		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;
	}

	private Class<? extends StandardEnvironment> deduceEnvironmentClass() {
		switch (this.webApplicationType) {
		case SERVLET:
			return StandardServletEnvironment.class;
		case REACTIVE:
			return StandardReactiveWebEnvironment.class;
		default:
			return StandardEnvironment.class;
		}
	}

  • Web Reactive: StandardEnvironment
  • Web Servlet: StandardServletEnvironment
  • 非 Web: StandardEnvironment

5、创建 Spring 应用上下文

根据构造阶段的推断出来的Web应用类型创建对应的 ConfigurableApplicationContext实例。

	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);
	}
  • Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
  • Web Servlet: AnnotationConfigServletWebServerApplicationContext
  • 非 Web: AnnotationConfigApplicationContext

总结

至此我们就完成了对Spring Boot项目启动的流程进行了简要的分析。我们可以知道SpringApplication主要分为两个阶段,一个是准备阶段主要进行Web类型推断、引导类推断、加载上下文初始化器和事件监听器,另一个是运行阶段主要进行加载运行SpringApplication的运行监听器、监听Spring Boot事件、创建Environment、创建Spring应用上下文。这样就完成了一个Spring Boot应用的启动。

原文链接:Spring Boot 启动深究SpringApplication执行流程Spring Boot启动初探的文章中,我们了解到@SpringBootApplication注解起到的作用。接下来我们探究一下SpringApplication.run()方法,在应用启动的背后都做了哪些事情。让我对Spring Boot的了解更深一层。

演示环境

  • IntelliJ IDEA 2018.2.5 (Ultimate Edition)
  • JDK 1.8
  • Spring Boot 2.1.1.RELEASE
  • Maven 3.5.4

初始化Spring Boot项目

初始化项目我们就不多阐述了,大家自行解决吧。当我们初始完项目后都会有一个类似的启动类,代码如下:

@SpringBootApplication
public class JeromeApplication {

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

启动项目就是运行这个main方法就行了,接下来我们就以SpringApplication.run(JeromeApplication.class, args);作为入口,来探究一下项目启动的时候Spring Boot到底做了些什么。

进入源码部分

当我们使用工具跟run方法的时候,会走到它一个重载方法在1258行:

	// 代码跟到这儿,会调用run方法的重载方法
	public static ConfigurableApplicationContext run(Class<?> primarySource,
			String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}	
	
	// 实际运行这个方法
	public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

这个方法里面首先要创建一个SpringApplication对象实例,然后调用这个创建好的SpringApplication的实例的run方法。

探究SpringApplication的构造阶段

当我们使用默认的方式启动项目,我们可以根据上面那段代码看出通过new SpringApplication(primarySources)创建了一个对象,我们进入到这个构造方法然后再跳到重载方法(263行):

	// 同样会运行重载方法
	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}	

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 1、进行Web应用的类型推断
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 2、加载应用上下文初始化器
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
        // 3、加载应用事件监听器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 4、推断引导类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

根据上面的代码我们来说一下构造阶段主要具体都干了写什么。

1、进行Web应用的类型推断

根据WebApplicationType这个类的静态方法deduceFromClasspath()推断Web应用类型,其实也就是根据当前的应用 ClassPath中是否存在相关实现类来推断 Web 应用的类型,类型有三种:

  • Web Reactive: WebApplicationType.REACTIVE
  • Web Servlet: WebApplicationType.SERVLET
  • 非 Web: WebApplicationType.NONE
	// 类的全路径的静态常量
	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";

	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;
	}

根据上面的代码可以看出推断的逻辑:

  • WEBFLUX_INDICATOR_CLASS存在并且WEBMVC_INDICATOR_CLASSJERSEY_INDICATOR_CLASS都不存在的时候,Web的类型才会被推断成Web Reactive。
  • WEBFLUX_INDICATOR_CLASSSERVLET_INDICATOR_CLASSES都不存在的时候,Web的类型才会被推断成非 Web类型。
  • 其它情况都会被推断成Web Servlet的类型,所以当Reactive和Servlet同时存在的时候,Reactive会被覆盖掉。

2、加载应用上下文初始化器

利用 Spring 工厂加载机制,实例化 ApplicationContextInitializer实现类,并排序对象集合。

	// 在这里传入的是ApplicationContextInitializer.class
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// 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;
	}

这里利用SpringFactoriesLoader来加载spring-boot-autoconfigure-2.1.1.RELEASE.jar包里面的META-INF/spring.factories配置文件,来读取key为ApplicationContextInitializer.class的全路径名org.springframework.context.ApplicationContextInitializer配置的值,然后再根据这些names去实例化这些对象,最后在根据Order进行排序。实现Order的两种方式:

  • 实现org.springframework.core.Ordered接口,重写int getOrder();方法。
  • 使用@Order注解进行标注。

3、加载应用事件监听器

利用 Spring 工厂加载机制,实例化 ApplicationListener实现类,并排序对象集合。其实这个和上下文的初始化器的加载时同样的原理。

    // 在这里传入的是ApplicationListener.class
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return getSpringFactoriesInstances(type, new Class<?>[] {});
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        // 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;
    }

同样是利用SpringFactoriesLoader来加载spring-boot-autoconfigure-2.1.1.RELEASE.jar包里面的META-INF/spring.factories配置文件,来读取key为ApplicationListener.class的全路径名org.springframework.context.ApplicationListener配置的值,然后再根据这些names去实例化这些对象,最后在根据Order进行排序。

4、推断引导类

利用异常的堆栈信息来推断引导类,也就是我们启动Spring Boot项目的那个main方法所在的类。

    private Class<?> deduceMainApplicationClass() {
       try {
          StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
          for (StackTraceElement stackTraceElement : stackTrace) {
             if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
             }
          }
       }
       catch (ClassNotFoundException ex) {
          // Swallow and continue
       }
       return null;
    }

通过异常的堆栈信息中方法名为main的栈帧来得到入口类的名字。

到这里SpringApplication就已经完成了在构造阶段所初始化的过程。

探究SpringApplication的运行阶段

当我们执行完new SpringApplication(primarySources)后,就会调用该对象的run方法。

	public ConfigurableApplicationContext run(String... args) {
        // 启动一个计时器
		StopWatch stopWatch = new StopWatch();
        // 开始计时
		stopWatch.start();
        // 定义局部变量
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        // 设置java.awt.headless系统属性为true - 没有图形化界面
		configureHeadlessProperty();
        // 1、加载SpringApplication运行监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
        // 2、运行SpringApplication运行监听器,Spring应用刚启动
		listeners.starting();
		try {
            // 根据传入的args,创建ApplicationArguments对象
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
            // 4、创建 Environment,这里调用listeners.environmentPrepared(ConfigurableEnvironment)
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
            // 打印Banner
			Banner printedBanner = printBanner(environment);
            // 5、创建Spring应用上下文
			context = createApplicationContext();
            // 异常分析报告的记录容器的初始化
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
            // Spring上下文前置处理,这里调用listeners.contextPrepared(context)和listeners.contextLoaded(context)
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
            // Spring上下文刷新
			refreshContext(context);
            // Spring上下文后置处理
			afterRefresh(context, applicationArguments);
            // 停止计时
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
            // Spring 应用已经启动
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
            // 处理异常,并会打印异常分析报告,并调用listeners.failed(context, exception);
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
            // Spring 应用正在运行
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

1、加载 SpringApplication运行监听器

这个加载运行监听器的方式,大家仔细一看就会发现,和初始化阶段加载应用上下文初始化器和事件监听器的方法如出一辙。

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

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// 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;
	}

通过上面的代码也可以看出,同样是利用的工厂加载机制读取SpringApplicationRunListener.class的全路径名org.springframework.boot.SpringApplicationRunListener配置的值,然后再根据这些names去实例化这些对象,最后在根据Order进行排序。获取这些对象的集合后会被封装到SpringApplicationRunListeners这个类里面。

2、运行 SpringApplication运行监听器

在这里SpringApplicationRunListener监听了多个运行状态方法,参照下表:

监听方法阶段说明Spring Boot 起始版本
starting()Spring 应用刚启动1.0
environmentPrepared(ConfigurableEnvironment)ConfigurableEnvironment 准备妥当,允许将其调整1.0
contextPrepared(ConfigurableApplicationContext)ConfigurableApplicationContext 准备妥当,允许将其调整1.0
contextLoaded(ConfigurableApplicationContext)ConfigurableApplicationContext 已装载,但仍未启动1.0
started(ConfigurableApplicationContext)ConfigurableApplicationContext 已启动,此时 Spring Bean 已初始化完成2.0
running(ConfigurableApplicationContext)Spring 应用正在运行2.0
failed(ConfigurableApplicationContext,Throwable)Spring 应用运行失败2.0

3、监听Spring Boot事件

我们在加载阶段,通过工厂机制加载了 SpringApplicationRunListener的实现类 EventPublishingRunListener,这个类利用SimpleApplicationEventMulticaster来广播 Spring Boot 事件。

EventPublishingRunListener监听方法与 Spring Boot事件对照表:

监听方法Spring Boot 事件Spring Boot 起始版本
starting()ApplicationStartingEvent1.5.0
environmentPrepared(ConfigurableEnvironment)ApplicationEnvironmentPreparedEvent1.0
contextPrepared(ConfigurableApplicationContext)ApplicationContextInitializedEvent2.1.0
contextLoaded(ConfigurableApplicationContext)ApplicationPreparedEvent1.0
started(ConfigurableApplicationContext)ApplicationStartedEvent2.0.0
running(ConfigurableApplicationContext)ApplicationReadyEvent1.3.0
failed(ConfigurableApplicationContext,Throwable)ApplicationFailedEvent1.0

4、创建 Environment

根据构造阶段的推断出来的Web应用类型创建对应的 ConfigurableEnvironment实例:

	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		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;
	}

	private Class<? extends StandardEnvironment> deduceEnvironmentClass() {
		switch (this.webApplicationType) {
		case SERVLET:
			return StandardServletEnvironment.class;
		case REACTIVE:
			return StandardReactiveWebEnvironment.class;
		default:
			return StandardEnvironment.class;
		}
	}

  • Web Reactive: StandardEnvironment
  • Web Servlet: StandardServletEnvironment
  • 非 Web: StandardEnvironment

5、创建 Spring 应用上下文

根据构造阶段的推断出来的Web应用类型创建对应的 ConfigurableApplicationContext实例。

	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);
	}
  • Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
  • Web Servlet: AnnotationConfigServletWebServerApplicationContext
  • 非 Web: AnnotationConfigApplicationContext

总结

至此我们就完成了对Spring Boot项目启动的流程进行了简要的分析。我们可以知道SpringApplication主要分为两个阶段,一个是准备阶段主要进行Web类型推断、引导类推断、加载上下文初始化器和事件监听器,另一个是运行阶段主要进行加载运行SpringApplication的运行监听器、监听Spring Boot事件、创建Environment、创建Spring应用上下文。这样就完成了一个Spring Boot应用的启动。

原文链接:Spring Boot 启动深究SpringApplication执行流程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值