Spring Boot源码解析:详解Spring Boot启动流程

在这里插入图片描述

新增扩展点

Spring Boot相对于Spring在启动流程上来说并没有太大的区别,只是借助于SpringApplication将启动过程模版化了,并在其中增加了批量注册,自动装配的功能,并开放了一系列的扩展点

对了还有一个重要的区别,原来基于servlet的web应用,是由servlet容器如tomcat来启动spring容器,现在是spring容器来启动servlet容器

SpringApplicationRunListener

SpringApplicationRunListener它可以在Spring Boot main方法启动过程中接收不同时间点的事件。

public interface SpringApplicationRunListener {

	// 开始启动
	default void starting() {
	}

	// environment准备好了
	default void environmentPrepared(ConfigurableEnvironment environment) {
	}

	// context准备好了
	default void contextPrepared(ConfigurableApplicationContext context) {
	}

	default void contextLoaded(ConfigurableApplicationContext context) {
	}

	default void started(ConfigurableApplicationContext context) {
	}

	default void running(ConfigurableApplicationContext context) {
	}

	default void failed(ConfigurableApplicationContext context, Throwable exception) {
	}

}

Spring Boot中SpringApplicationRunListener的实现类就有一个EventPublishingRunListener,基本就是在各个时间点发布相应的事件,如ApplicationStartingEvent,ApplicationEnvironmentPreparedEvent等,有需要的化可以监听这些事件扩展启动的过程

当我们想用SpringApplicationRunListener进行扩展时,在当前应用的classpath下的META-INF/spring.factories中配置key为org.springframework.boot.SpringApplicationRunListener的实现类即可

CommandLineRunner

CommandLineRunner是Spring Boot特有的扩展接口,和它类似的还有一个ApplicationRunner接口

CommandLineRunner和ApplicationRunner会在ApplicationContext完全启动后开始执行
可以利用这个接口做一些初始化工作,或者打印加载容器中的Bean,方便排查问题

@SpringBootApplication
public class DemoApplication {

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

    @Bean
    public CommandLineRunner commandLineRunner(ApplicationContext context) {
	    return args -> {
            String[] bns = context.getBeanDefinitionNames();
            for (String beanName : bns) {
                System.out.println(beanName);
            }
        };
    }
}

如果你对Spring Boot启动过程中的扩展点的默认实现感兴趣的话,可以看一下spring.factories文件

在这里插入图片描述

SpringApplication的创建

Spring Boot的启动过程大致可以分为如下4步(基于Servlet容器的we应用)

  1. SpringApplication的创建
  2. SpringApplication的启动
  3. WebServer的创建与启动
  4. DispatcherServlet的注册

在这里插入图片描述
primarySource是启动的配置类
在这里插入图片描述
推断web容器的方式比较简单,看classpath中是否有相应的类
在这里插入图片描述
SpringApplication的创建比较简单,画图总结一下
在这里插入图片描述

SpringApplication的启动

在这里插入图片描述
SpringApplication的源码就不追了,大概就是这样一个启动流程
在这里插入图片描述
其中通告starting,就是执行SpringApplicationRunListener#starting方法。以此类推

WebServer的创建与启动

启动Application的过程其实就是执行AbstractApplicationContext#refresh方法,这个方法我们之前仔细分析过了哈,就是一个启动的模版方法,子类可以基于这个方法进行扩展。本节我们就简单看一下针对servlet这种容器,在启动的过程中做了哪些扩展!

在这里插入图片描述

在这里插入图片描述
ServletWebServerApplicationContext#createWebServer
在这里插入图片描述
创建的时候传入了一个ServletContextInitializer接口的实现类,这个类后面会用到哈,先在这里提一下

ServletWebServerApplicationContext#getSelfInitializer
在这里插入图片描述

画图总结一下WebServer的创建与启动过程
在这里插入图片描述

DispatcherServlet的注册

将DispatcherServlet注册到WebServer的过程主要分为如下2个部分

  1. DispatcherServlet被注册到Spring容器中
  2. ServletRegistrationBean包装了DispatcherServlet,并将DispatcherServlet注册到WebServer中
DispatcherServlet怎么被注册到Spring容器中的

很简单,在DispatcherServlet的无参构造方法上加一个断点

在这里插入图片描述
看下一个调用栈,实在DispatcherServletAutoConfiguration中被注入进来的
在这里插入图片描述
这个配置类在spring.factories文件中,所以Spring Boot启动就会加载这个类,但这只是将DispatcherServlet注入到spring容器,所以一定是有其他的Bean把DispatcherServlet注入到Servlet容器中的,查找到用了DispatcherServlet类的Bean就只有DispatcherServletRegistrationBean
在这里插入图片描述
那估计就是DispatcherServletRegistrationBean将DispatcherServlet注入到Servlet容器了

为什么DispatcherServletRegistrationBean能把DispatcherServlet注入到Servelt容器中呢?

这时候就不得不提我们在Spring MVC的启动流程中提到的ServletContainerInitializer接口了,Servlet3.0之后Servlet容器启动后得回调ServletContainerInitializer#onStartup方法,这是规范得遵守。

在tomcat启动后会调用TomcatStarter#onStartup方法,接着调用ServletContextInitializer#onStartup方法
在这里插入图片描述
在这里插入图片描述
请添加图片描述
DispatcherServletRegistrationBean实现了ServletContextInitializer接口,在onStartup方法中将servlet注入到servlet容器

在这里插入图片描述
我直接总结一下把,你对着我的流程图debug就懂了

  1. 通过DispatcherServletAutoConfiguration往spring容器中注入DispatcherServlet
  2. DispatcherServlet被包装为DispatcherServletRegistrationBean,用于向Servlet容器中注册
  3. 在Tomcat创建阶段,将ServletContainerInitializer接口的实现类(即selfInitialize方法)设置到TomcatStarter中
  4. 把TomcatStarter注册到Tomcat中
  5. Tomcat启动后,会调用ServletContainerInitializer#onStartup,即调用TomcatStarter#onStartup,接着调用ServletWebServerApplicationContext#selfInitialize
  6. selfInitialize方法回调ServletContextInitializer#onStartup方法
  7. DispatcherServletRegistrationBean实现类ServletContextInitializer接口,所以会调用父类的
    RegistrationBean#onStartup方法
  8. 在RegistrationBean#onStartup方法中会把内部维护的Servlet,Listener,Filter注册到ServletContext中

参考博客

[1]

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java识堂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值