SpringBoot源码学习(一) 启动流程解析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: SpringBoot 源码解析系列

索引


前言

  • java社区里面spring全家桶一直是支柱一般的存在。 其中springboot甚至已经不能以火热来形容了, 甚至已经形成了行业某种意义上的”标准“ 。 所以了解并探究springboot的实现是非常有意义的。
  • 本文基于springboot 2.0.4, spring5 的源代码进行分析。

正文

启动第一个springboot应用

  • 下载测试工程
  • 在IDEA上导入项目 并执行SpringBootDemoApplication.class中的main方法
  • 结果如下


如何启动的

  • 通过代码我们可以看到启动一个springboot应用,非常简单。 只需要一行代码

SpringApplication.run(SpringBootDemoApplication.class, args);


  • debug进去发现其实就是首先new了一个SpringApplication实例,再执行该实例的run方法. 如图


new 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 = deduceWebApplicationType();

setInitializers((Collection) getSpringFactoriesInstances(

  ApplicationContextInitializer.class));

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

this.mainApplicationClass = deduceMainApplicationClass();

}

  • 在new SpringApplication 的时候核心是做了两件事 1. 判断当前spring运行的环境 2. 加载META-INF/spring.factories 并初始化监听器
  • 判断当前spring运行的环境
  • 我们debug到源码里面可以看到, 其实就是根据当前的运行环境有没有某个类来判断的

private WebApplicationType deduceWebApplicationType() {

if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)

  && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)

  && !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {

  return WebApplicationType.REACTIVE;

}

for (String className : WEB_ENVIRONMENT_CLASSES) {

  if (!ClassUtils.isPresent(className, null)) {

  return WebApplicationType.NONE;

  }

}

return WebApplicationType.SERVLET;

}

  • 简单来说,目前springboot支持三种运行环境

 public enum WebApplicationType {


/**

 * The application should not run as a web application and should not start an

 * embedded web server.

        * 简单来说就是没有交互的应用程序

 */

NONE,


/**

 * The application should run as a servlet-based web application and should start an

 * embedded servlet web server.

        * 普通的WEB应用

 */

SERVLET,


/**

 * The application should run as a reactive web application and should start an

 * embedded reactive web server.

        * 响应式的WEB应用 还比较新 提供了很多新特性 spring4刚刚支持

 */

REACTIVE


  }

  • 可以通过加载不同starter来切换环境


  • 加载META-INF/spring.factories
  • 实际上setInitializers, setListeners这两个方法就是扫描spring.factories类并把实例加载到内存中

执行.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();

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;

}

其中第一个比较核心是 这两行 获取并启动监听器


 SpringApplicationRunListeners listeners = getRunListeners(args);

 listeners.starting();


  • 首先springboot会首先获取SpringApplicationRunListeners 根据spring.factories的定义 实际上实例化的时候是EventPublishingRunListener这个listener
  • 看一下EventPublishingRunListener的构造函数

public EventPublishingRunListener(SpringApplication application, String[] args) {

this.application = application;

this.args = args;

this.initialMulticaster = new SimpleApplicationEventMulticaster();

for (ApplicationListener<?> listener : application.getListeners()) {

  this.initialMulticaster.addApplicationListener(listener);

}

}

  • 在这里会把所有通过spring.factories实例化的listener添加到SimpleApplicationEventMulticaster中。
  • 第二步执行starting方法, 会发布一个ApplicationStartingEvent
  • 我们继续跟进进去,发现了另外一个核心的方法multicastEvent, 我们查一下 有四个listener注册了ApplicationStartingEvent这个事件
  • 我们随便找了一个LoggingApplicationListener 看一下核心代码

   @Override

   public void onApplicationEvent(ApplicationEvent event) {

       //在springboot启动的时候

       if (event instanceof ApplicationStartedEvent) {

           onApplicationStartedEvent((ApplicationStartedEvent) event);

       }

       //springboot的Environment环境准备完成的时候

       else if (event instanceof ApplicationEnvironmentPreparedEvent) {

           onApplicationEnvironmentPreparedEvent(

                   (ApplicationEnvironmentPreparedEvent) event);

       }

       //在springboot容器的环境设置完成以后

       else if (event instanceof ApplicationPreparedEvent) {

           onApplicationPreparedEvent((ApplicationPreparedEvent) event);

       }

       //容器关闭的时候

       else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)

               .getApplicationContext().getParent() == null) {

           onContextClosedEvent();

       }

       //容器启动失败的时候

       else if (event instanceof ApplicationFailedEvent) {

           onApplicationFailedEvent();

       }

   }


  • springboot如何判断一个listener支持什么事件。 debug进去可以发现是通过GenericApplicationListener 这个接口实现的
  • 因为我们的事件类型为ApplicationEvent,所以会执行onApplicationStartedEvent((ApplicationStartedEvent) event);。springBoot会在运行过程中的不同阶段,发送各种事件,来执行对应监听器的对应方法。

小结

  • 根据上文,我们可以简单的做一下小结,在执行了SpringApplication.run(SpringBootDemoApplication.class, args)后发生了什么
  • new了一个SpringApplication实例
  • 判断当前spring运行的环境
  • 加载META-INF/spring.factories 并初始化监听器
  • SpringApplications实例.run
  • 获取并启动监听器
  • 实例化EventPublishingRunListener
  • 把所有通过spring.factories实例化的listener添加到SimpleApplicationEventMulticaster中。
  • 发布ApplicationStartingEvent 执行注册了这个事件的listener
  • 后面还会进行
  • 构造容器环境
  • 创建容器
  • 实例化Failure Analyzers 处理启动的报错信息
  • 准备容器
  • 刷新容器
  • 刷新容器的后扩展接口
  • 后面因为涉及到了spring的核心容器部分,会单独开几章来讲,敬请期待~
目录
相关文章
|
21天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
176 37
|
9天前
|
JavaScript Java 关系型数据库
毕设项目&课程设计&毕设项目:基于springboot+vue实现的在线考试系统(含教程&源码&数据库数据)
本文介绍了一个基于Spring Boot和Vue.js实现的在线考试系统。随着在线教育的发展,在线考试系统的重要性日益凸显。该系统不仅能提高教学效率,减轻教师负担,还为学生提供了灵活便捷的考试方式。技术栈包括Spring Boot、Vue.js、Element-UI等,支持多种角色登录,具备考试管理、题库管理、成绩查询等功能。系统采用前后端分离架构,具备高性能和扩展性,未来可进一步优化并引入AI技术提升智能化水平。
毕设项目&课程设计&毕设项目:基于springboot+vue实现的在线考试系统(含教程&源码&数据库数据)
|
11天前
|
Java 关系型数据库 MySQL
毕设项目&课程设计&毕设项目:springboot+jsp实现的房屋租租赁系统(含教程&源码&数据库数据)
本文介绍了一款基于Spring Boot和JSP技术的房屋租赁系统,旨在通过自动化和信息化手段提升房屋管理效率,优化租户体验。系统采用JDK 1.8、Maven 3.6、MySQL 8.0、JSP、Layui和Spring Boot 2.0等技术栈,实现了高效的房源管理和便捷的租户服务。通过该系统,房东可以轻松管理房源,租户可以快速找到合适的住所,双方都能享受数字化带来的便利。未来,系统将持续优化升级,提供更多完善的服务。
毕设项目&课程设计&毕设项目:springboot+jsp实现的房屋租租赁系统(含教程&源码&数据库数据)
|
2天前
|
程序员 C++
C++编程:While与For循环的流程控制全解析
总结而言,`while`循环和 `for`循环各有千秋,它们在C++编程中扮演着重要的角色。选择哪一种循环结构应根据具体的应用场景、循环逻辑的复杂性以及个人的编程风格偏好来决定。理解这些循环结构的内在机制和它们之间的差异,对于编写高效、易于维护的代码至关重要。
9 1
|
7天前
|
存储 缓存 Java
在Spring Boot中使用缓存的技术解析
通过利用Spring Boot中的缓存支持,开发者可以轻松地实现高效和可扩展的缓存策略,进而提升应用的性能和用户体验。Spring Boot的声明式缓存抽象和对多种缓存技术的支持,使得集成和使用缓存变得前所未有的简单。无论是在开发新应用还是优化现有应用,合理地使用缓存都是提高性能的有效手段。
14 1
|
9天前
|
监控 数据挖掘 BI
项目管理流程全解析及关键步骤介绍
项目管理流程是项目成功的基石,涵盖启动、规划、执行、监控和收尾等阶段。Zoho Projects 等软件可提高效率,支持结构化启动与规划、高效执行与协作及实时监控。这些流程和工具对项目的全局视角、团队协作和风险控制至关重要。项目管理软件适用于不同规模企业,实施时间因软件复杂度和企业准备而异。
25 2
|
16天前
|
机器学习/深度学习 数据采集 JavaScript
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
ADR药品不良反应监测系统是一款智能化工具,用于监测和分析药品不良反应。该系统通过收集和分析病历、处方及实验室数据,快速识别潜在不良反应,提升用药安全性。系统采用Java开发,基于SpringBoot框架,前端使用Vue,具备数据采集、清洗、分析等功能模块,并能生成监测报告辅助医务人员决策。通过集成多种数据源并运用机器学习算法,系统可自动预警药品不良反应,有效减少药害事故,保障公众健康。
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
|
2月前
|
持续交付 jenkins Devops
WPF与DevOps的完美邂逅:从Jenkins配置到自动化部署,全流程解析持续集成与持续交付的最佳实践
【8月更文挑战第31天】WPF与DevOps的结合开启了软件生命周期管理的新篇章。通过Jenkins等CI/CD工具,实现从代码提交到自动构建、测试及部署的全流程自动化。本文详细介绍了如何配置Jenkins来管理WPF项目的构建任务,确保每次代码提交都能触发自动化流程,提升开发效率和代码质量。这一方法不仅简化了开发流程,还加强了团队协作,是WPF开发者拥抱DevOps文化的理想指南。
49 1
|
26天前
|
缓存 网络协议 Linux
DNS的执行流程是什么?
DNS的执行流程是什么?
32 0

推荐镜像

更多
下一篇
无影云桌面