的run代码_SpringApplication.run() 源码分析

我们都知道使用 Spring Boot 创建一个 Java 项目非常的简单,Spring 官网也给出了 Quickstart 示例。 只需要写几行代码,就可以让项目运行起来。在这简单的背后,是 Spring 默默的做了很多事情。今天我们就来研究一下 Spring 在背后为我们做了哪些事情。

以下是 Spring 官网的 Quickstart 示例代码:

@SpringBootApplication
public class DemoApplication {

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

项目的启动入口是:SpringApplication.run(),代码跟踪进去发现,Spring 主要做了两步操作:

  1. 初始化 SpringApplication;
  2. 执行 SpringApplication 的 run()方法;

9a1e5c437563aa4232d3d796bdf0d0a7.png
SpringApplication.run() 执行 时序图

继续深入,我们首先看看 第一步 :初始化 SpringApplication:

SpringApplication 初始化主要做了以下6件事情:

6c2557022a69f68e2aab5c9cc216f85f.png
SpringApplication 初始化 流程图

初始化 SpringApplication 的核心代码,如下:

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

SpringApplication 初始化完成之后,进入第二步:执行 SpringApplication 的 run()方法:

整个 run() 方法的执行过程,如下图所示:

e6f173082acf79a51f749ab9f705d7d8.png
SpringApplication run() 方法执行流程图

其中 「准备 context」和「刷新 context」的过程较长,上图右半部分单独列出来了。

run()方法的核心代码,如下:

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();

  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);
  listeners.running(context);
  return context;
}

「准备 context」的核心代码,在 SpringApplication 类中,如下:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
    SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
  context.setEnvironment(environment);
  postProcessApplicationContext(context);
  applyInitializers(context);
  listeners.contextPrepared(context);
  if (this.logStartupInfo) {
    logStartupInfo(context.getParent() == null);
    logStartupProfileInfo(context);
  }
  // Add boot specific singleton beans
  ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
  if (printedBanner != null) {
    beanFactory.registerSingleton("springBootBanner", printedBanner);
  }
  if (beanFactory instanceof DefaultListableBeanFactory) {
    ((DefaultListableBeanFactory) beanFactory)
        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
  }
  if (this.lazyInitialization) {
    context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
  }
  // Load the sources
  Set<Object> sources = getAllSources();
  Assert.notEmpty(sources, "Sources must not be empty");
  load(context, sources.toArray(new Object[0]));
  listeners.contextLoaded(context);
}

「刷新 context」的核心代码,在 AbstractApplicationContext 类中,如下:

public void refresh() throws BeansException, IllegalStateException {
  synchronized(this.startupShutdownMonitor) {
      this.prepareRefresh();
      ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
      this.prepareBeanFactory(beanFactory);
      this.postProcessBeanFactory(beanFactory);
      this.invokeBeanFactoryPostProcessors(beanFactory);
      this.registerBeanPostProcessors(beanFactory);
      this.initMessageSource();
      this.initApplicationEventMulticaster();
      this.onRefresh();
      this.registerListeners();
      this.finishBeanFactoryInitialization(beanFactory);
      this.finishRefresh();
      this.resetCommonCaches();
  }
}

以上就是项目启动时,执行SpringApplication run()方法的执行过程。如需更详细的了解,可以在 IDE 中,从项目入口开始,一步一步查看 Spring 源码;希望大家有所收获,奥利给~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值