SpringBoot启动整体分析

SpringBoot版本:2.2.9.RELEASE

启动入口

启动时调用SpringApplication#run(java.lang.Class<?>, java.lang.String...)方法

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

run方法中将Class对象以数组参数传入SpringApplication重载的run方法

1
2
3
4
5
6
7
8
9
10
/**
 * Static helper that can be used to run a {@link SpringApplication} from the
 * specified source using default settings.
 * @param primarySource the primary source to load
 * @param args the application arguments (usually passed from a Java main method)
 * @return the running {@link ApplicationContext}
 */
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
  return run(new Class<?>[] { primarySource }, args);
}

重载的run方法中创建了一个SpringApplication对象,之后调用SpringApplication#run(java.lang.String...)方法,下面重点分析这两步操作。

1
2
3
4
5
6
7
8
9
10
/**
 * Static helper that can be used to run a {@link SpringApplication} from the
 * specified sources using default settings and user supplied arguments.
 * @param primarySources the primary sources to load
 * @param args the application arguments (usually passed from a Java main method)
 * @return the running {@link ApplicationContext}
 */
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
  return new SpringApplication(primarySources).run(args);
}

创建SpringApplication对象步骤

①将MyApplication启动类的Class保存在SpringApplication对象的成员变量primarySources中。

②deduceFromClasspath通过deduceFromClasspath推论当前应用的类型,WebApplicationType共定义了三种类型:

NONE(不作为web应用启动,不启动内嵌服务),SERVLET(基于servlet的web应用启动,启动内嵌servlet web服务),REACTIVE(响应式web应用启动,启动内嵌的响应式web服务)

③getSpringFactoriesInstances加载META-INF/spring.factories文件中org.springframework.context.ApplicationContextInitializer对应的一系列Class并实例化对象,将实例化对象以集合存储在SpringApplication对象的成员变量initializers中

④getSpringFactoriesInstances加载META-INF/spring.factories文件中org.springframework.context.ApplicationListener对应的一系列Class并实例化对象,将实例化对象以集合存储在SpringApplication对象的成员变量listeners中

⑤deduceMainApplicationClass推论出main方法所在的启动类Class并保存在SpringApplication对象的成员变量mainApplicationClass中

复制代码
/**
   * Create a new {@link SpringApplication} instance. The application context will load
   * beans from the specified primary sources (see {@link SpringApplication class-level}
   * documentation for details. The instance can be customized before calling
   * {@link #run(String...)}.
   * @param resourceLoader the resource loader to use
   * @param primarySources the primary bean sources
   * @see #run(Class, String[])
   * @see #setSources(Set)
   */
  @SuppressWarnings({ "unchecked", "rawtypes" })
  public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
  }
复制代码

META-INF/spring.factories中的ApplicationContextInitializer

1
2
3
4
5
6
7
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

META-INF/spring.factories中的ApplicationListener

复制代码
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
复制代码

SpringApplication#run方法执行流程

①stopWatch.start()通过StopWatch开启计时器

②configureHeadlessProperty设置系统属性java.awt.headless

③getRunListeners获取运行监听器,加载META-INF/spring.factories文件中org.springframework.boot.SpringApplicationRunListener对应的org.springframework.boot.context.event.EventPublishingRunListener并实例化对象,将实例化对象保存在SpringApplicationRunListeners对象的成员变量listeners中

④listeners.starting()发布启动事件(ApplicationStartingEvent)

⑤prepareEnvironment创建环境Environment,并将配置文件信息设置到环境中

⑥configureIgnoreBeanInfo设置系统属性spring.beaninfo.ignore,配置需要忽略的BeanInfo

⑦printBanner打印Banner

⑧createApplicationContext()通过策略方法创建应用上下文

⑨getSpringFactoriesInstances‍ 加载META-INF/spring.factories文件中org.springframework.boot.SpringBootExceptionReporter对应的Class并实例化对象,获取异常报告器实例

⑩prepareContext准备应用上下文

⑪refreshContext刷新应用上下文,进行spring容器初始化

⑫afterRefresh刷新应用上下文后置操作,空方法支持扩展

⑬stopWatch.stop() stopWatch计时结束

⑭listeners.started发布应用上下文启动完成事件(ApplicationStartedEvent)

⑮callRunners执行Runner运行器(ApplicationRunner和CommandLineRunner)

⑯listeners.running发布应用上下文就绪事件(ApplicationReadyEvent)

⑰返回应用上下文

复制代码
/**
   * 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;
  }
复制代码

 

posted @   sunnycony  阅读(22)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示