【Springboot】有了解过SpringBoot的启动流程吗?

简介: 面试官: 有了解过SpringBoot的启动流程吗?没有啊,行,那今天面试就到这里了

SpringBoot启动原理


每次看到这个问题总是不能理出一条线来回答流畅,这次理出一条八股文标准答案出来。复习的时候拿出来过一过思路。如果有不合适的地方希望各位大佬指教~


[源码基于springboot2.4.3]


框架启动类


每个SpringBoot项目都有一个标注着@SpringBootApplication注解的main启动类,

@SpringBootApplicationpublicclassApplication {
publicstaticvoidmain(String[] args) throwsException {
SpringApplication.run(Application.class, args);
    }
}


直接看SpringApplication.run方法,往下跟两下发现整个启动流程分为两部分,一个SpringBootApplication构造方法和运行run方法


publicstaticConfigurableApplicationContextrun(Class<?>[] primarySources, String[] args) {
returnnewSpringApplication(primarySources).run(args);
}


核心注解

@SpringBootApplication注解点开之后一堆注解,只需要关注三个核心注解:


@SpringBootConfiguration

  1. 本质上是包装了一层@Configuration注解,通过JavaConfig方式对bean进行配置,标识当前类为配置类。


@EnableAutoConfiguration

  1. 点开@EnableAutoConfiguration注解,只看两个核心注解AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)
  2. @AutoConfigurationPackage,这个注解跟@Component注解含义类似,一个用来手动添加注解加载自动配置类一个用来扫描指定路径注入bean
  1. @AutoConfigurationPackage是为了在一个路径下面有多个自动配置的类要加载,这个注解就不用对每个类单独添加 @Import了,直接引入包路径更方便
  2. @ComponentScan方便对包路径进行自定义,比如一些 Bean 跟 SpringBootApplication 不在一个路径下面。
  1. @Import(AutoConfigurationImportSelector.class),AutoConfigurationImportSelector类中getCandidateConfigurations()方法,这个方法通过SpringFactoriesLoader.loadFactoryNames()查找位于META-INF/spring.factories文件中的所有自动配置类并加载这些类

@ComponentScan

  1. 默认扫描当前包以及子包,将有`@Component`,`@Controller`,`@Service`,`@Repository`等注解的类注册到容器中,以便调用。


以上的注解只是将需要加载类加载到classpath中,真正去获取类转换成bean的过程还是在run方法中,所以建议回答问题的时候可以先提一下核心注解,启动流程主要还是SpringBootApplcation类的构造方法和run方法。


构造方法

publicSpringApplication(ResourceLoaderresourceLoader, Class<?>... primarySources) {
this.resourceLoader=resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources=newLinkedHashSet<>(Arrays.asList(primarySources));
// 获取应用类型,根据是否加载Servlet类判断是否是web环境this.webApplicationType=WebApplicationType.deduceFromClasspath();
this.bootstrappers=newArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
// 读取META-INFO/spring.factories文件,获取对应的ApplicationContextInitializer装配到集合setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置所有监听器setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断main函数this.mainApplicationClass=deduceMainApplicationClass();
}


可以看到构造方法里主要做了这么几件事:

  1. 根据是否加载servlet类判断是否是web环境
  2. 获取所有初始化器,扫描所有`META-INF/spring.factories`下的ApplicationContextInitializer子类通过反射拿到实例,在spring实例启动前后做一些回调工作。
  3. 获取所有监听器,同2,也是扫描配置加载对应的类实例。
  4. 定位main方法


run方法

/*** 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}*/publicConfigurableApplicationContextrun(String... args) {
// 启动一个秒表计时器,用于统计项目启动时间StopWatchstopWatch=newStopWatch();
stopWatch.start();
// 创建启动上下文对象即spring根容器DefaultBootstrapContextbootstrapContext=createBootstrapContext();
// 定义可配置的应用程序上下文变量ConfigurableApplicationContextcontext=null;
/*** 设置jdk系统属性* headless直译就是无头模式,* headless模式的意思就是明确Springboot要在无鼠键支持的环境中运行,一般程序也都跑在Linux之类的服务器上,无鼠键支持,这里默认值是true;*/configureHeadlessProperty();
/*** 获取运行监听器 getRunListeners, 其中也是调用了上面说到的getSpringFactoriesInstances 方法* 从spring.factories中获取配置*/SpringApplicationRunListenerslisteners=getRunListeners(args);
// 启动监听器listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 包装默认应用程序参数,也就是在命令行下启动应用带的参数,如--server.port=9000ApplicationArgumentsapplicationArguments=newDefaultApplicationArguments(args);
///*** 准备环境 prepareEnvironment 是个硬茬,里面主要涉及到* getOrCreateEnvironment、configureEnvironment、configurePropertySources、configureProfiles* environmentPrepared、bindToSpringApplication、attach诸多方法可以在下面的例子中查看*/ConfigurableEnvironmentenvironment=prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 配置忽略的 beanconfigureIgnoreBeanInfo(environment);
// 打印 SpringBoot 标志,即启动的时候在控制台的图案logo,可以在src/main/resources下放入名字是banner的自定义文件BannerprintedBanner=printBanner(environment);
// 创建 IOC 容器context=createApplicationContext();
// 设置一个启动器,设置应用程序启动context.setApplicationStartup(this.applicationStartup);
// 配置 IOC 容器的基本信息 (spring容器前置处理)prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
/*** 刷新IOC容器* 这里会涉及Spring容器启动、自动装配、创建 WebServer启动Web服务即SpringBoot启动内嵌的 Tomcat*/refreshContext(context);
/*** 留给用户自定义容器刷新完成后的处理逻辑* 刷新容器后的扩展接口(spring容器后置处理)*/afterRefresh(context, applicationArguments);
// 结束计时器并打印,这就是我们启动后console的显示的时间stopWatch.stop();
if (this.logStartupInfo) {
// 打印启动完毕的那行日志newStartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
// 发布监听应用上下文启动完成(发出启动结束事件),所有的运行监听器调用 started() 方法listeners.started(context);
// 执行runner,遍历所有的 runner,调用 run 方法callRunners(context, applicationArguments);
        } catch (Throwableex) {
// 异常处理,如果run过程发生异常handleRunFailure(context, ex, listeners);
thrownewIllegalStateException(ex);
        }
try {
// 所有的运行监听器调用 running() 方法,监听应用上下文listeners.running(context);
        } catch (Throwableex) {
// 异常处理handleRunFailure(context, ex, null);
thrownewIllegalStateException(ex);
        }
// 返回最终构建的容器对象returncontext;
    }

这一套连招下来谁能背得下来。。。八股文讲究的就是通俗好背,你知道自己是背的,面试官也知道你是背的,但这层窗户纸就不能捅破,到时候去面试背到一半卡壳了多尴尬。


就背最重要的几个点,反正其他不痛不痒的地方背了也会忘掉。要说最重要的地方当属AbstractApplicationConetxt.refresh方法。


refresh方法


这里就不在粘代码了,直接总结成几句话吧,代码多了记不住。refresh方法贯穿bean的生命周期


invokeBeanFactoryPostProcessors


  1. invokeBeanFactoryPostProcessors方法会找出beanFactory中所有的实现了**BeanDefinitionRegistryPostProcessor**接口和**BeanFactoryPostProcessor**接口的bean并执行postProcessor中的postProcessBeanDefinitionRegistry()方法和postProcessBeanFactory()方法
  2. 调用doProcessConfigurationClass方法会处理所有SpringBoot注解标注的所有类,如@Import、@Bean等注解。
  3. 调用BeanDefinitionRegistryPostProcessor实现向容器内添加bean的定义, 调用BeanFactoryPostProcessor实现向容器内添加bean的定义添加属性。(SpringBean的生命周期)


onRefresh

  1. 创建web容器。如果是web环境当中,会构建一个tomcat web容器。



总结

写多了也背不过来,总结一下整体的springboot的启动步骤。


  1. 整个spring框架启动分为两部分,`构造SpringBootApplication对象`和执行`run方法`
  2. 核心注解@SpringBootConfiguration标识启动类为配置类,@EnableAutoConfiguration通过内部@Import注解AutoConfigurationImportSelector.class实现自动装配,@ComponentScan默认扫描当前目录及子目录下的bean。
  3. SpringBootApplication的构造方法主要做了几件事。
  1. 根据是否加载servlet类判断是否是web环境
  2. 获取所有初始化器,扫描所有`META-INF/spring.factories`下的ApplicationContextInitializer子类通过反射拿到实例,在spring实例启动前后做一些回调工作。
  3. 获取所有监听器,同2,也是扫描配置加载对应的类实例。
  4. 定位main方法
  1. run方法主要创建了配置环境、事件监听、启动应用上下文,其中rfresh方法贯穿springbean的生命周期,执行bean的生命周期的前后置钩子方法,并且处理spring的注解标注的类。在onRefresh中通过Java代码构建出tomcat容器并启动。
  2. 好了,回家等通知吧







相关文章
|
1月前
|
前端开发 Java 应用服务中间件
SpringBoot-Run启动流程
探索Spring Boot启动流程,从加载配置、创建应用上下文、自动配置到启动内嵌服务器。启动类是入口点,`@SpringBootApplication`标记启动,`SpringApplication.run`启动应用。自动配置基于条件注解配置Bean,应用上下文由`SpringApplication`创建并刷新。内嵌服务器如Tomcat随应用启动,简化部署。理解此流程有助于深入掌握Spring Boot。
179 2
|
1月前
|
缓存 Java 程序员
springboot的启动流程总结
springboot的启动流程总结
|
1月前
|
设计模式 Java 容器
SpringBoot2 | SpringBoot启动流程源码分析(二)
SpringBoot2 | SpringBoot启动流程源码分析(二)
50 0
|
1月前
|
设计模式 Java 机器人
SpringBoot3自动配置流程 SPI机制 核心注解 自定义starter
SpringBoot3自动配置流程 SPI机制 核心注解 自定义starter
|
2天前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的销售项目流程化管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的销售项目流程化管理系统附带文章源码部署视频讲解等
16 3
|
18天前
|
JavaScript Java 测试技术
基于springboot+vue.js的工作流程管理系统附带文章和源代码设计说明文档ppt
基于springboot+vue.js的工作流程管理系统附带文章和源代码设计说明文档ppt
13 0
|
22天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp微信小程序的工作流程管理系统的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的工作流程管理系统的详细设计和实现
|
1月前
|
Java Spring 容器
深入理解Spring Boot启动流程及其实战应用
【5月更文挑战第9天】本文详细解析了Spring Boot启动流程的概念和关键步骤,并结合实战示例,展示了如何在实际开发中运用这些知识。
36 2
|
1月前
|
安全 NoSQL Java
SpringBoot集成Activiti5.22在线流程设计器
SpringBoot集成Activiti5.22在线流程设计器
79 0
SpringBoot集成Activiti5.22在线流程设计器
|
1月前
|
缓存 Java 开发者
10个点介绍SpringBoot3工作流程与核心组件源码解析
Spring Boot 是Java开发中100%会使用到的框架,开发者不仅要熟练使用,对其中的核心源码也要了解,正所谓知其然知其所以然,V 哥建议小伙伴们在学习的过程中,一定要去研读一下源码,这有助于你在开发中游刃有余。欢迎一起交流学习心得,一起成长。