在JarLauncher根据MAINFEST.MF 文件中的Start-class的类名找到我们定义的SpringBoot应用,通过反射执行其main方法之后, 程序开始调用 SpringApplicaiton的静态run方法执行,形如下面的示例代码

  1. @SpringBootApplication
  2. public class SpringbootApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(SpringBootApplication.class,args);
  5. }
  6. }

SpringApplication 源码分析

首先给出源码代码,可以看到, run(Class<?> primarySource, String... args) 调用
run(Class<?>[] primarySources, String... args) 传递的是多个primarySource 然后调用了使用new指令创建了 primarySource 其构造方法的签名为 public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)

SprintApplication 源码分析 - 图1 在构造方法中主要实现了一下内容:

  • 推测当前应用类型,主要是根据ClassPath是否存在Servlet或者其他相应的类来推断
  • 在当前 ClassPath中寻找META-INF/spring.factories 文件,根据文件内容找到 ApplicationContextInitializer 并初始化实例对象
  • 在当前 ClassPath中寻找META-INF/spring.factories 文件,根据文件内容找到 ApplicationListener 并初始化实例对象
  • [x] 推断出指定的main方法的类型 ```java

    public static ConfigurableApplicationContext run(Class<?> primarySource, String… args) {

    1. return run(new Class<?>[] { primarySource }, args);

    }

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {

    1. return new SpringApplication(primarySources).run(args);

    }

    public SpringApplication(Class<?>… primarySources) {

    1. this(null, primarySources);

    }

    public SpringApplication(ResourceLoader resourceLoader, Class<?>… primarySources) {

    1. this.resourceLoader = resourceLoader;
    2. Assert.notNull(primarySources, "PrimarySources must not be null");
    3. this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    4. // 推断当前应用类型
    5. this.webApplicationType = WebApplicationType.deduceFromClasspath();
    6. // 查找ApplicationContextInitializer,创建实例对象设置给当前上下文的成员变量
    7. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    8. // 查找 ApplicationListener,创建实例对象设置给当前上下文的成员变量
    9. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    10. // 推断出main方法所在的类,非常巧妙
    11. this.mainApplicationClass = deduceMainApplicationClass();

    }

// 推断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) {} return null; } // 推荐Web应用的类型

  1. private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
  2. private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
  3. private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
  4. private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
  5. private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
  6. static WebApplicationType deduceFromClasspath() {
  7. // 包含DispatcherHandler类并且不包含 DispatcherServlet & ServletContainer 则认为是 REACTIVE(Spring5提供)
  8. if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
  9. && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
  10. return WebApplicationType.REACTIVE;
  11. }
  12. // 如果不包含 DispatcherServlet & ServletContainer 认为是NODE
  13. for (String className : SERVLET_INDICATOR_CLASSES) {
  14. if (!ClassUtils.isPresent(className, null)) {
  15. return WebApplicationType.NONE;
  16. }
  17. }
  18. // 否则认为是 SERVLET
  19. return WebApplicationType.SERVLET;
  20. }
  1. <a name="YvewX"></a>
  2. ### 初始化 ApplicationContextInitializer
  3. ```java
  4. // 获取Spring工厂实例集合
  5. private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
  6. // 获取类加载器,如果给定的资源类加载器不为null,则使用资源的类加载器,否在使用线程上下文类加载器
  7. // 如果上下文中不存在类加载器,则使用系统类加载器,详情参考 ClassUtils.getDefaultClassLoader();
  8. ClassLoader classLoader = getClassLoader();
  9. // 根据指定的类类型信息,查找到多个 META-INF/spring.factories 中定义的 ApplicationContextInitializer的类名
  10. // 在这里的type = ApplicationContextInitializer.class
  11. Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
  12. // 根据全类名和参数创建Spring工厂实例
  13. List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
  14. AnnotationAwareOrderComparator.sort(instances);
  15. return instances;
  16. }
  17. public ClassLoader getClassLoader() {
  18. if (this.resourceLoader != null) {
  19. return this.resourceLoader.getClassLoader();
  20. }
  21. return ClassUtils.getDefaultClassLoader();
  22. }
  23. // =====================================================================================
  24. // 查找到多个 META-INF/spring.factories 中定义的 ApplicationContextInitializer的类名
  25. public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
  26. String factoryTypeName = factoryType.getName();
  27. // 从所有的定义中找到符合当前type信息的类集合
  28. return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
  29. }
  30. //查找出 所有的 spring.factories 文件中定义的类信息
  31. private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
  32. MultiValueMap<String, String> result = cache.get(classLoader);
  33. if (result != null) {
  34. return result;
  35. }
  36. try {
  37. Enumeration<URL> urls = (classLoader != null ?
  38. classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
  39. ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
  40. result = new LinkedMultiValueMap<>();
  41. while (urls.hasMoreElements()) {
  42. URL url = urls.nextElement();
  43. UrlResource resource = new UrlResource(url);
  44. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  45. for (Map.Entry<?, ?> entry : properties.entrySet()) {
  46. String factoryTypeName = ((String) entry.getKey()).trim();
  47. for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
  48. result.add(factoryTypeName, factoryImplementationName.trim());
  49. }
  50. }
  51. }
  52. cache.put(classLoader, result);
  53. return result;
  54. }
  55. catch (IOException ex) {
  56. throw new IllegalArgumentException("Unable to load factories from location [" +
  57. FACTORIES_RESOURCE_LOCATION + "]", ex);
  58. }
  59. }
  60. // 根据给到的类型信息等参数,创建实例对象,并返回
  61. private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
  62. ClassLoader classLoader, Object[] args, Set<String> names) {
  63. List<T> instances = new ArrayList<>(names.size());
  64. for (String name : names) {
  65. try {
  66. Class<?> instanceClass = ClassUtils.forName(name, classLoader);
  67. Assert.isAssignable(type, instanceClass);
  68. Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
  69. T instance = (T) BeanUtils.instantiateClass(constructor, args);
  70. instances.add(instance);
  71. }
  72. catch (Throwable ex) {
  73. throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
  74. }
  75. }
  76. return instances;
  77. }

初始化 ApplicationListener

初始化 ApplicationListener 的过程和上述过程非常类似,这里不再赘述,只是传递的type信息不一致,和 ApplicationContextInitializer 一样。

  1. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

Run 方法分析

在成功构造SpringApplication之后,会调用run方法执行,首先来看一下run 方法的源码内容.

  • 第一步:获取并启动监听器
  • 第二步:构造容器环境
  • 第三步:创建容器
  • 第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
  • 第五步:准备容器
  • 第六步:刷新容器
  • 第七步:刷新容器后的扩展接口
  1. /**
  2. * Run the Spring application, creating and refreshing a new
  3. * {@link ApplicationContext}.
  4. * @param args the application arguments (usually passed from a Java main method)
  5. * @return a running {@link ApplicationContext}
  6. */
  7. public ConfigurableApplicationContext run(String... args) {
  8. // 创建一个StopWatch
  9. StopWatch stopWatch = new StopWatch();
  10. stopWatch.start();
  11. ConfigurableApplicationContext context = null;
  12. Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  13. // 配置HeadLess环境
  14. configureHeadlessProperty();
  15. // 和 ApplicationContextInitializer & ApplicationListener一样,生成对象 SpringApplicationRunListener
  16. // Spring应用运行回调监听器,会在Spring应用不同的声明周期执行相应的方法, 并且运行他们的starting方法
  17. SpringApplicationRunListeners listeners = getRunListeners(args);
  18. listeners.starting();
  19. try {
  20. ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  21. ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  22. configureIgnoreBeanInfo(environment);
  23. Banner printedBanner = printBanner(environment);
  24. context = createApplicationContext();
  25. exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
  26. new Class[] { ConfigurableApplicationContext.class }, context);
  27. prepareContext(context, environment, listeners, applicationArguments, printedBanner);
  28. refreshContext(context);
  29. afterRefresh(context, applicationArguments);
  30. stopWatch.stop();
  31. if (this.logStartupInfo) {
  32. new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
  33. }
  34. listeners.started(context);
  35. callRunners(context, applicationArguments);
  36. }
  37. catch (Throwable ex) {
  38. handleRunFailure(context, ex, exceptionReporters, listeners);
  39. throw new IllegalStateException(ex);
  40. }
  41. try {
  42. listeners.running(context);
  43. }
  44. catch (Throwable ex) {
  45. handleRunFailure(context, ex, exceptionReporters, null);
  46. throw new IllegalStateException(ex);
  47. }
  48. return context;
  49. }

ConfigurableApplicationContext context = null;

一个SpringBoot上下文提供了一下功能

  • Bean factory methods for accessing application components. Inherited from ListableBeanFactory.
  • The ability to load file resources in a generic fashion. Inherited from the org.springframework.core.io.ResourceLoader interface.
  • The ability to publish events to registered listeners. Inherited from the ApplicationEventPublisher interface.
  • The ability to resolve messages, supporting internationalization. Inherited from the MessageSource interface.
  • Inheritance from a parent context. Definitions in a descendant context will always take priority. This means, for example, that a single parent context can be used by an entire web application, while each servlet has its own child context that is independent of that of any other servlet
  1. public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
  2. MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
  3. // ....
  4. }

SpringApplicationRunListeners listeners = getRunListeners(args);

SpringApplicationRunListeners 对象内部是众多 SpringApplicationRunListener 对象,当调用其starting方法的会后,会使用 EventPublishingRunListener 发送SpringApplicationEvent

  1. @Override
  2. public void starting() {
  3. this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
  4. }
  5. // 其中ApplicationStartingEvent的定义如下
  6. @SuppressWarnings("serial")
  7. public class ApplicationStartingEvent extends SpringApplicationEvent {
  8. /**
  9. * Create a new {@link ApplicationStartingEvent} instance.
  10. * @param application the current application
  11. * @param args the arguments the application is running with
  12. */
  13. public ApplicationStartingEvent(SpringApplication application, String[] args) {
  14. super(application, args);
  15. }
  16. }

实际上,SpringApplicationEvent 有众多的实现类,分别对应了不同的SpringApplication的生命周期事件。 image.png

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

这里构造了应用参数对象,使用的默认JVM参数,记录了应用的激活的profile以及应用的配置源信息

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  • 创建一个可配置的环境对象,环境是Spring中非常重要的一个组件,代表着当前应用正在运行的环境
  • 传递 listeners 是为了触发SpringApplicationEvent事件,即上图的第二个实现,ApplicationEnvironmentPreparedEvent
  1. private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
  2. ApplicationArguments applicationArguments) {
  3. // 创建并配置一个环境,根据不同的应用类型来配置
  4. ConfigurableEnvironment environment = getOrCreateEnvironment();
  5. configureEnvironment(environment, applicationArguments.getSourceArgs());
  6. ConfigurationPropertySources.attach(environment);
  7. listeners.environmentPrepared(environment);
  8. bindToSpringApplication(environment);
  9. if (!this.isCustomEnvironment) {
  10. environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
  11. deduceEnvironmentClass());
  12. }
  13. ConfigurationPropertySources.attach(environment);
  14. return environment;
  15. }

Banner printedBanner = printBanner(environment);
  • 打印输出Banner的信息
  • 根据 bannerMode (默认值: CONSOLE ) 来调用不同的流程来处理日志信息
  1. private Banner printBanner(ConfigurableEnvironment environment) {
  2. if (this.bannerMode == Banner.Mode.OFF) {
  3. return null;
  4. }
  5. ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
  6. : new DefaultResourceLoader(getClassLoader());
  7. SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
  8. if (this.bannerMode == Mode.LOG) {
  9. return bannerPrinter.print(environment, this.mainApplicationClass, logger);
  10. }
  11. return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
  12. }
  13. // 默认的banner的文件名称
  14. public class SpringApplicationBannerPrinter{
  15. static final String DEFAULT_BANNER_LOCATION = "banner.txt";
  16. //.....
  17. }
  18. // 获取Banner的过程,返回的默认Banner DEFAULT_BANNER 则就是 SpringBootBanner
  19. private Banner getBanner(Environment environment) {
  20. Banners banners = new Banners();
  21. banners.addIfNotNull(getImageBanner(environment));
  22. banners.addIfNotNull(getTextBanner(environment));
  23. if (banners.hasAtLeastOneBanner()) {
  24. return banners;
  25. }
  26. if (this.fallbackBanner != null) {
  27. return this.fallbackBanner;
  28. }
  29. return DEFAULT_BANNER;
  30. }

在代码执行完成之后,控制台会输出

  1. . ____ _ __ _ _
  2. /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
  3. ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
  4. \\/ ___)| |_)| | | | | || (_| | ) ) ) )
  5. ' |____| .__|_| |_|_| |_\__, | / / / /
  6. =========|_|==============|___/=/_/_/_/
  7. :: Spring Boot :: (v2.2.6.RELEASE)

常用的接口声明

  • [x] ApplicationListener 应用监听器 监听Spring应用的各种事件 ```java @FunctionalInterface public interface ApplicationListener extends EventListener {

    /**

    • Handle an application event.
    • @param event the event to respond to */ void onApplicationEvent(E event);

}

  1. - [x] ApplicationContextInitializer 应用上下文初始化
  2. ```java
  3. public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
  4. /**
  5. * Initialize the given application context.
  6. * @param applicationContext the application to configure
  7. */
  8. void initialize(C applicationContext);
  9. }
  • [x] SpringApplicationRunListener Spring应用运行周期的先关回调 ```java public interface SpringApplicationRunListener {

    // 早起初始化的时候启动 default void starting() { }

    // 当环境信息准备好的时候执行一次
    default void environmentPrepared(ConfigurableEnvironment environment) { }

    // 调用一次,当应用上下文被创建并且准备好,但信息被在载入之前执行
    default void contextPrepared(ConfigurableApplicationContext context) { }

    // 当应用上下文加载完成,并在refreshed之前调用 default void contextLoaded(ConfigurableApplicationContext context) { }

    // 上下文加载并刷新完成已经started,但 CommandLineRunners & ApplicationRunner 未被调用之前 default void started(ConfigurableApplicationContext context) { }

    // 上下文被加载之前并且CommandLineRunners & ApplicationRunner 被调用之后 default void running(ConfigurableApplicationContext context) { }

    default void failed(ConfigurableApplicationContext context, Throwable exception) { }

}

```