源码剖析-springboot启动类

发布于 2021-12-06  238 次阅读


随着微服务的发展,springboot已经不可或缺,也许你会想为什么点击启动类就能启动整个服务,他们内部到底是怎么运作的,能够如此方便快捷,那么本文我们就来看看这个 SpringApplication 以及 run() 方法 是如何加载的,它背后又隐藏了哪些奥秘呢?

@SpringBootApplication
public class SpringCloud01Application {

public static void main(String[] args) {
/*SpringApplication sa=new SpringApplication(SpringCloud01Application.class);
sa.setInitializers();//用户自定义的扩展
sa.run(args);*/
SpringApplication.run(SpringCloud01Application.class, args);
}

}

一、SpringApplication实例的初始化

首先new SpringApplication()对象,内部有四步操作

四个关键的步骤已标注在图中,分别解释如下:

deduceFromClasspath() 推断应用的类型:创建的是REACTIVE应用、SERVLET应用、NONE 三种中的某一种

setInitializers 使用SpringFactoriesLoader查找并加载classpath下META-INF/spring.factories文件中所有可用的 ApplicationContextInitializer

 setListeners使用SpringFactoriesLoader查找并加载classpath下META-INF/spring.factories文件中的所有可用的 ApplicationListener

 推断并设置main方法的定义类

二、SpringApplication的run()方法探秘

整个方法的目的是创建 applicationcontext ,从该方法能看到, SpringApplicationRunListeners 监听了整个生命周期,主要包括几个核心方法,除此之外,一些扩展占据了加载applicationcontext生命周期的大半江山

/**
 * 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();
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   ConfigurableApplicationContext context = null;
   configureHeadlessProperty();
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      context.setApplicationStartup(this.applicationStartup);
      prepareContext(bootstrapContext, 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, listeners);
      throw new IllegalStateException(ex);
   }

   try {
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

流程图

我们将各步骤总结精炼如下:

  1. 通过 SpringFactoriesLoader 加载 META-INF/spring.factories 文件,获取并创建 SpringApplicationRunListener 对象
  2. 然后由 SpringApplicationRunListener 来发出 starting 消息
  3. 创建参数,并配置当前 SpringBoot 应用将要使用的 Environment
  4. 完成之后,依然由 SpringApplicationRunListener 来发出 environmentPrepared 消息
  5. 创建 ApplicationContext
  6. 初始化 ApplicationContext,并设置 Environment,加载相关配置等
  7. 由 SpringApplicationRunListener 来发出 contextPrepared 消息,告知Spring Boot 应用使用的 ApplicationContext 已准备OK
  8. 将各种 beans 装载入 ApplicationContext,继续由 SpringApplicationRunListener 来发出 contextLoaded 消息,告知 Spring Boot 应用使用的 ApplicationContext 已装填OK
  9. refresh ApplicationContext,完成IoC容器可用的最后一步
  10. 由 SpringApplicationRunListener 来发出 started 消息
  11. 调用callRunners(...)方法,让实现了ApplicationRunnerCommandLineRunner接口类的run 方法得以执行,用于在 Spring 应用上下文准备完毕后,执行一些额外操作。从而完成最终的程序的启动。
  12. 由 SpringApplicationRunListener 来发出 running 消息,告知程序已运行起来了

Springboot揭秘 快速构建微服务体系一书中有如下图,说明了springboot的启动过程

该文参考了你知道Spring Boot项目是怎么启动的吗? (qq.com)