> 文档中心 > 【SpringBoot】一、SpringBoot启动流程源码分析

【SpringBoot】一、SpringBoot启动流程源码分析

上一篇 【Spring】六、Spring与MyBatis整合核心源码分析

下一篇【SpringBoot】二、SpringBoot自动配置原理

文章目录

  • SpringBoot启动流程(SpringBoot启动原理)
  • 宏观步骤:(概括为18个小步骤)
  • SpringBoot启动流程源码分析
        • 1.入口main方法执行;
        • 2.、执行run方法;
        • 3.看一下run(new Class[] { primarySource }, args);方法;
        • 4、先看做的第一件事情,new了一个SpringApplication对象
        • 5、然后我们看做的第二件事情,执行new出来的SpringApplication对象的run()方法;
        • 6、接下来我们重点看一下run方法里面的几个重要步骤,首先看看红色标记的创建spring容器上下文context = createApplicationContext();这段代码主要是根据项目类型创建Spring上下文,并且会注入几个核心组件类;
        • 7、然后再看一下run方法里面另一个比较重要的步骤,红色标记的创建和刷新spring容器refreshContext()方法,refreshContext(context) 这个方法,该方法启动spring的代码加载了bean,还启动了内嵌的web容器;
        • 8.再看onRefresh()方法
  • 流程图

SpringBoot启动流程(SpringBoot启动原理)

启动流程主要分为三个部分:

  • 第一部分进行SpringApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监听器;(了解一下)
  • 第二部分实现了应用具体的启动方案,包括启动流程的监听模块、加载配置环境模块、及核心的创建Spring上下文环境模块;(掌握)
  • 第三部分是自动化配置模块,该模块是SpringBoot自动配置的核心;(掌握)

宏观步骤:(概括为18个小步骤)

  • 1、创建并启动计时监控类StopWatch
  • 2、初始化应用上下文和异常报告集合(ConfigurableApplicationContext和Collection)
  • 3、设置系统属性 configureHeadlessProperty()
  • 4、创建Spring运行的监听器并启动
  • 5、初始化默认应用参数类
  • 6、准备Spring环境
  • 7、创建beanner打印类
  • 8、创建应用Spring上下文(也就是spring容器)
  • 9、准备异常报告器
  • 10、准备应用上下文(也就是spring容器)
  • 11、刷新上下文(也就是spring容器)
  • 12、应用上下文刷新后置处理(也就是spring容器)
  • 13、停止计时监控类
  • 14、输出日志信息
  • 15、发布应用上下文启动完成事件
  • 16、执行所有Runner运行器(如果实现CommandLineRunner接口)
  • 17、发布应用上下文就绪事件
  • 18、完成返回容器对象

SpringBoot启动流程源码分析

1.入口main方法执行;

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

2.、执行run方法;

public static ConfigurableApplicationContext run(Class<?> primarySource,String... args) { //调用run() 方法return run(new Class<?>[] { primarySource }, args);}

3.看一下run(new Class[] { primarySource }, args);方法;

public static ConfigurableApplicationContext run(Class<?>[] primarySources,String[] args) {return new SpringApplication(primarySources).run(args);}//这个run方法主要做两件事情//1、new了一个SpringApplication 这么一个对象;(1)把main方法这个类赋给一个成员变量;(2)判断是java项目还是web项目;(3)设置了一个初始化器和事件发送的监听器;//2、然后执行SpringApplication对象的run()方法;

4、先看做的第一件事情,new了一个SpringApplication对象

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");//1、先把主类保存起来this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//2、判断运行项目的类型this.webApplicationType = WebApplicationType.deduceFromClasspath();//3、扫描当前路径下META-INF/spring.factories文件的,加载ApplicationContextInitializer接口实例setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//4、扫描当前路径下META-INF/spring.factories文件的,加载ApplicationListener接口实例setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //判断main方法的类this.mainApplicationClass = deduceMainApplicationClass();}这里面用到了一种类似于Java SPI机制的方式扫描META-INF/spring.factories这个文件,并且加载?ApplicationContextInitializerApplicationListener 接口实例;(1ApplicationContextInitializer 这个类当springboot上下文Context初始化完成后会调用;(2ApplicationListener 当springboot启动时事件change后都会触发我们可以自定义实现ApplicationContextInitializerApplicationListener 接口,然后运行时会被触发执行;

5、然后我们看做的第二件事情,执行new出来的SpringApplication对象的run()方法;

public ConfigurableApplicationContext run(String... args) {    <!--这是一个计时器,不是很重要-->StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();<!--这是设置了一些环境变量信息,不是很重要-->configureHeadlessProperty();<!--获取事件监听器SpringApplicationRunListener类型,并且执行starting()方法-->SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {<!--把参数args封装成DefaultApplicationArguments,了解一下即可-->ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);<!--这个是准备环境,并且把环境跟spring上下文绑定好,并且执行environmentPrepared()方法-->ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);<!--判断一些环境的值,并设置一些环境的值-->configureIgnoreBeanInfo(environment);<!--打印banner-->Banner printedBanner = printBanner(environment);<!--创建spring ioc上下文,根据项目类型创建上下文-->context = createApplicationContext();<!--获取异常报告事件监听-->exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);<!--准备spring ioc上下文,执行完成后调用contextPrepared()方法,contextLoaded()方法-->prepareContext(context, environment, listeners, applicationArguments,printedBanner);<!--这是刷新和创建spring ioc容器,这里会扫描bean定义并初始化单例bean对象-->//这个refreshContext()不仅创建bean,还启动了内嵌的web容器,非常重要refreshContext(context);<!--什么事都没有做-->afterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}<!--执行ApplicationRunListeners中的started()方法-->listeners.started(context);<!--执行RunnerApplicationRunnerCommandLineRunner-->callRunners(context, applicationArguments);}catch (Throwable ex) {handleRunFailure(context, listeners, exceptionReporters, ex);throw new IllegalStateException(ex);}listeners.running(context);return context;}

6、接下来我们重点看一下run方法里面的几个重要步骤,首先看看红色标记的创建spring容器上下文context = createApplicationContext();这段代码主要是根据项目类型创建Spring上下文,并且会注入几个核心组件类;

protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);}}catch (ClassNotFoundException ex) {throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);}}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);}public AnnotationConfigServletWebServerApplicationContext() {//1:会去注入一些spring核心组件   this.reader = new AnnotatedBeanDefinitionReader(this);   this.scanner = new ClassPathBeanDefinitionScanner(this);}Web类型项目创建上下文对象AnnotationConfigServletWebServerApplicationContext,这里会把?ConfigurationClassPostProcessorAutowiredAnnotationBeanPostProcessor 等一些核心组件加入到Spring容器,这其实已经是spring ioc容器的源码,如果了解spring ioc源码,那么对springboot源码的阅读会有很大的帮助;

7、然后再看一下run方法里面另一个比较重要的步骤,红色标记的创建和刷新spring容器refreshContext()方法,refreshContext(context) 这个方法,该方法启动spring的代码加载了bean,还启动了内嵌的web容器;

private void refreshContext(ConfigurableApplicationContext context) {//刷新和创建spring容器refresh(context);if (this.registerShutdownHook) {try {context.registerShutdownHook();}catch (AccessControlException ex) {// Not allowed in some environments.}}}protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);//进入refresh()方法((AbstractApplicationContext) applicationContext).refresh();}

8.再看onRefresh()方法

接下来进入到Spring ioc容器刷新和创建的12个步骤的源码中,AbstractApplicationContext - >refresh()方法,在这12个步骤中,这里主要看一下onRefresh() 这个方法,但是发现这个方法里面什么都没有,显然是一个钩子方法,它会钩到它子类重写onRefresh()方法,所以去看子类里面的onRefresh()方法;
由于我们这里是一个Web项目,所以就可以看到 ServletWebServerApplicationContext 这个类,这个类下面的 onRefresh() 方法:

protected void onRefresh() {super.onRefresh();try {//看到了内嵌web容器的影子createWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}}private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {//1、获取webServerFactoryServletWebServerFactory factory = getWebServerFactory();this.webServer = factory.getWebServer(getSelfInitializer());}else if (servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context",ex);}}initPropertySources();}//继续看下getWebServletFactory() 这个方法,这里面选择出哪种类型的web容器protected ServletWebServerFactory getWebServerFactory() {// Use bean names so that we don't consider the hierarchyString[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);if (beanNames.length == 0) {throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "+ "ServletWebServerFactory bean.");}if (beanNames.length > 1) {throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "+ "ServletWebServerFactory beans : "+ StringUtils.arrayToCommaDelimitedString(beanNames));}return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);}// 再回头去看factory.getWebServer(getSelfInitializer()) ,转到定义就会看到很熟悉的名字tomcatpublic WebServer getWebServer(ServletContextInitializer... initializers) {//看到了我们熟悉的tomcatTomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null ? this.baseDirectory: createTempDir("tomcat"));tomcat.setBaseDir(baseDir.getAbsolutePath());Connector connector = new Connector(this.protocol);tomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);configureEngine(tomcat.getEngine());for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}prepareContext(tomcat.getHost(), initializers);return getTomcatWebServer(tomcat);}内置的Servlet容器就是在onRefresh() 方法里面启动的,至此一个Servlet容器就启动OK了;

流程图

在这里插入图片描述