spring-boot-mvc启动流程
spring-boot-mvc启动流程
1、前言
在web应用开发中,我们比较常用的是spring boot+spring mvc的组合。该组合的核心点就是内嵌tomcat和mvc的dispatcherServlet,今天,我们就来探究一下其启动流程。
前置知识点:spring boot原理、spring mvc原理
源码版本:spring boot 2.1.7
2、流程
此处先给出结论,抓住主线,便于后面的学习。
核心步骤分四步:
- 容器类型确定
- 创建web容器
- 启动内嵌tomcat
- 启动DispatcherServlet
大致时序图如下:
3、容器类型
容器类型,由WebApplicationType枚举类定义:
web容器
- mvc容器
- reactive容器
非web容器
SpringApplication的构造方法中设置
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;。。。。//调用方法设置容器类型this.webApplicationType = WebApplicationType.deduceFromClasspath();。。。。this.mainApplicationClass = deduceMainApplicationClass();
}
WebApplicationType.deduceFromClasspath()获取容器类型
static WebApplicationType deduceFromClasspath() {if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {//reactivereturn WebApplicationType.REACTIVE;}for (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {//普通容器return WebApplicationType.NONE;}}//返回servlet容器,即mvcreturn WebApplicationType.SERVLET;
}
- WebApplicationType.REACTIVE - 当类路径中存在REACTIVE_WEB_ENVIRONMENT_CLASS并且不存在MVC_WEB_ENVIRONMENT_CLASS时
- WebApplicationType.NONE - 也就是非Web型应用(Standard型),此时类路径中不包含WEB_ENVIRONMENT_CLASSES中定义的任何一个类时
- WebApplicationType.SERVLET - 类路径中包含了WEB_ENVIRONMENT_CLASSES中定义的所有类型时
4、创建web容器
在上步,我们已经确定了容器的类型,接下来就是创建容器了。核心步骤:
- 根据类型获取定义的Class
- 通过反射创建容器
源码如下:
**SpringApplication.run()方法中的createApplicationContext()**执行
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();...try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//准备环境,确认activefile,确认配置的文件的内容全部可用,此处会准备bootstarpapplication,ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);. ...//创建容器context = createApplicationContext();...prepareContext(context, environment, listeners, applicationArguments, printedBanner);//刷新容器,关键点,tomcat在这里面的onrefresh中创建refreshContext(context);afterRefresh(context, applicationArguments);stopWatch.stop();...return context;
}
createApplicationContext()方法。根据容器类型创建容器,mvc的容器为ServletWebServerApplicationContext。
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass;//根据容器type加载classif (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET://需要的servletWeb容器的Class,mvccontextClass = 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) {...}}//根据class创建并返回web容器return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
5、 启动tomcat
我们观察项目启动的日志,会有tomcat的相关日志。debug找到相关的类,便于确定调用堆栈。
Debug的调用堆栈如下:
我们跟着调用堆栈走,先看看onRefrensh()方法。
5.1、onRefresh
ServletWebServerApplicationContext实现了onRefresh的方法,该方法在AbstractApplicationContext无实现,交由子类扩展。
实现逻辑如下:
protected void onRefresh() {super.onRefresh();try {//创建webServercreateWebServer();}catch (Throwable ex) {throw new ApplicationContextException("Unable to start web server", ex);}
}
5.2、createWebServer
该方法中通过工厂获取webServer,根据不同的类型,servlet容器是tomcat。
private void createWebServer() {WebServer webServer = this.webServer;ServletContext servletContext = getServletContext();if (webServer == null && servletContext == null) {//通过工厂获取,先加载工厂,此处是tomcatServletWebServerFactoryServletWebServerFactory factory = getWebServerFactory();//工厂生产产品,tomcatServerthis.webServer = factory.getWebServer(getSelfInitializer());}else if (servletContext != null) {try {getSelfInitializer().onStartup(servletContext);}catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context", ex);}}initPropertySources();
}
5.2、 getWebServerFactory
然后通过容器getBean获取工厂bean,如果还没有,就会创建,进入getBean---->createBean的逻辑。在获取tomcatServletWebServerFactory中,引入了DispatcherServlet
protected ServletWebServerFactory getWebServerFactory() {//名字是tomcatServletWebServerFactoryString[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);....//先获取工厂,然后通过getBean获取工厂bean,此处是tomcatServletWebServerFactoryreturn getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
5.2.1 tomcatServletWebServerFactory注入
上图,获取tomcatServletWebServerFactory工厂,肯定需要该工厂的bean定义,那么定义在哪呢?
是在ServletWebServerFactoryConfiguration.EmbeddedTomcat中注入的,源码如下:
class ServletWebServerFactoryConfiguration {@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)public static class EmbeddedTomcat {//配置TomcatServletWebServerFactory的bean,用于创建tomcatServer@Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,ObjectProvider<TomcatContextCustomizer> contextCustomizers,ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();......return factory;}}
}
5.2.2、ServletWebServerFactoryConfiguration.EmbeddedTomcat
ServletWebServerFactoryConfiguration.EmbeddedTomcat是由ServletWebServerFactoryAutoConfiguration注入的。
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
//是在auto-configuation中注入的该类
@EnableConfigurationProperties(ServerProperties.class)
//注入了tomcat的工厂
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,//tomcat相关ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {@Beanpublic ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new ServletWebServerFactoryCustomizer(serverProperties);}//tomcat的自定义工厂,@Bean@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);}//注册后置处理器public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {private ConfigurableListableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (beanFactory instanceof ConfigurableListableBeanFactory) {this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;}}//bean的后置处理器,errorPageRegistrarBeanPostProcessor最后挂到了DispatchServlet上@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this.beanFactory == null) {return;}registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor.class);registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class);}}}
ServletWebServerFactoryAutoConfiguration是在auto-configuration的配置文件中注入的。
可以看见图片中,大多servlet相关的自动注入都在此处。
5.2.3、errorPageRegistrarBeanPostProcessor
创建tomcatServletWebServerFactory后对其进行后置处理时,用到了errorPageRegistrarBeanPostProcessor,该后置处理器初始化时最后调用了DispatchServlet
errorPageRegistrarBeanPostProcessor会获取register
private Collection<ErrorPageRegistrar> getRegistrars() {if (this.registrars == null) {// Look up does not include the parent contextthis.registrars = new ArrayList<>(//引入了ErrorPageRegistrar.class,关键点this.beanFactory.getBeansOfType(ErrorPageRegistrar.class, false, false).values());this.registrars.sort(AnnotationAwareOrderComparator.INSTANCE);this.registrars = Collections.unmodifiableList(this.registrars);}return this.registrars;}
5.2.4、ErrorPageRegistrar
唯一实现ErrorPageCustomizer,构造方法需要DispatcherServletPath的类
private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered {private final ServerProperties properties;//DispatcherServletPathprivate final DispatcherServletPath dispatcherServletPath;//引入DispatcherServletPath,关键点protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {this.properties = properties;this.dispatcherServletPath = dispatcherServletPath;}
}
3.2.5、DispatcherServletPath
DispatcherServletRegistrationBean是DispatcherServletPath的实现,构造方法引入了DispatcherServlet
/*** ServletRegistrationBean for the auto-configured DispatcherServlet. Both registers the servlet and exposes * DispatcherServletPath information.
*/
public class DispatcherServletRegistrationBean extends ServletRegistrationBean<DispatcherServlet>implements DispatcherServletPath {private final String path;//DispatcherServlet,关键类public DispatcherServletRegistrationBean(DispatcherServlet servlet, String path) {super(servlet);Assert.notNull(path, "Path must not be null");this.path = path;super.addUrlMappings(getServletUrlMapping());}}
5.2.6 DispatcherServlet
DispatcherServlet的由DispatcherServletAutoConfiguration注入如下:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";@Configuration(proxyBeanMethods = false)@Conditional(DefaultDispatcherServletCondition.class)@ConditionalOnClass(ServletRegistration.class)@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })protected static class DispatcherServletConfiguration {//关键//注入了dispatcherServlet@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {DispatcherServlet dispatcherServlet = new DispatcherServlet();......return dispatcherServlet;}}
}
DispatcherServletAutoConfiguration也是由spring.factories的auto-configura注入
5.3、getWebServer
获取webServer,在该方法中,tomcat出场了。
public WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();}//tomcat出场Tomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());//连接器Connector connector = new Connector(this.protocol);connector.setThrowOnFailure(true);//servicetomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);//engineconfigureEngine(tomcat.getEngine());for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}prepareContext(tomcat.getHost(), initializers);//创建tomcatwebserverreturn getTomcatWebServer(tomcat);
}
5.3.1 getTomcatWebServer
获取TomcatWebServer,原生的tomcat启动有些类似。
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {Assert.notNull(tomcat, "Tomcat Server must not be null");this.tomcat = tomcat;this.autoStart = autoStart;//初始化initialize();
}
//初始化tomcat
private void initialize() throws WebServerException {logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));synchronized (this.monitor) {try {addInstanceIdToEngineName();Context context = findContext();context.addLifecycleListener((event) -> {if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {// Remove service connectors so that protocol binding doesn't// happen when the service is started.removeServiceConnectors();}});//熟悉的地方// Start the server to trigger initialization listenersthis.tomcat.start();// We can re-throw failure exception directly in the main threadrethrowDeferredStartupExceptions();try {ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());}catch (NamingException ex) {// Naming is not enabled. Continue}// Unlike Jetty, all Tomcat threads are daemon threads. We create a// blocking non-daemon to stop immediate shutdownstartDaemonAwaitThread();}catch (Exception ex) {stopSilently();destroySilently();throw new WebServerException("Unable to start embedded Tomcat", ex);}}
}
6、 初始化DispatcherServlet
tomcatServer启动时还没有初始化DispatcherServlet,9大组件都未初始化,等到有请求进来了,再去初始化DispatcherServlet。DispatcherServlet的初始化在onRefresh中:
//刷新
protected void onRefresh(ApplicationContext context) {//初始化initStrategies(context);
}//初始化
protected void initStrategies(ApplicationContext context) {//9大组件initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);
}
6.1、onRefrsh
onRefresh由父类FrameworkServlet的initWebApplicationContext()调用,初始化逻辑此处不介绍。
protected WebApplicationContext initWebApplicationContext() {//web容器WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {// A context instance was injected at construction time -> use itwac = this.webApplicationContext;....}if (!this.refreshEventReceived) {// Either the context is not a ConfigurableApplicationContext with refresh// support or the context injected at construction time had already been// refreshed -> trigger initial onRefresh manually here.synchronized (this.onRefreshMonitor) {//关键刷新onRefresh(wac);}}if (this.publishContext) {// Publish the context as a servlet context attribute.String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;
}
6.2、调用栈
具体的调用堆栈如下:
由下至上可以看出:
- NioEndpoint接受到请求,交由Processor处理。
- 不同的协议是不同的Processor处理。此处是Http11Processor,处理请求,封装Request对象,然后交由Engine引擎
- Engine解析Host
- Host确定容器
- 容器确定Wrappper,Wrapper就是Servlet的包装。此处就是DispatchServlet
- 然后DispatchServlet初始化并分发请求。
spring-boot-mvc启动流程相关推荐
- Spring Boot的启动流程
文章目录 Spring Boot Spring Boot概念 Spring Boot的启动流程 1. 构造SpringApplection的实例 2. 调用实例的run方法 Spring Boot启动 ...
- Spring Boot项目启动流程
概述 用过Spring Boot的应该都知道,在项目启动入口的主类main()方法里,一句简简单单的 SpringApplication.run( ... ); 便开启了项目的启动运行之路. 本文我们 ...
- spring boot应用启动原理分析
spring boot quick start 在spring boot 里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启动的,不需要另外配置一个W ...
- java按需读取word文件_干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结...
我(梦在旅途,http://zuowj.cnblogs.com; http://www.zuowenjun.cn)最近发表的一篇文章<.NET CORE与Spring Boot编写控制台程序应有 ...
- 记一次 Spring Boot 项目启动卡住问题排查记录
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | 陈凯玲 来源 | https://url.cn ...
- Spring Boot————应用启动时的监听机制测试
引言 本文承接前面的<Spring Boot----Spring Boot启动流程分析>,主要测试一下ApplicationContextInitializer.SpringApplica ...
- java和asp.net core_干货分享:ASP.NET CORE(C#)与Spring Boot MVC(JAVA)异曲同工的编程方式总结...
目录 我(梦在旅途,http://zuowj.cnblogs.com; http://www.zuowenjun.cn)最近发表的一篇文章<.NET CORE与Spring Boot编写控制台程 ...
- Spring Boot(18)---启动原理解析
Spring Boot(18)---启动原理解析 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会 ...
- ASP.NET Core MVC 源码学习:MVC 启动流程详解
前言 在 上一篇 文章中,我们学习了 ASP.NET Core MVC 的路由模块,那么在本篇文章中,主要是对 ASP.NET Core MVC 启动流程的一个学习. ASP.NET Core 是新一 ...
- Spring Boot定制启动图案
转载自 Spring Boot定制启动图案 启动图案 Spring Boot在启动的时候会显示一个默认的Spring的图案,对应的类为SpringBootBanner. . ____ ...
最新文章
- echarts 默认显示图例_echarts图例组件点击显示功能(默认功能点击消失)
- iOS开发-编译出错 duplicate symbols for architecture x86_64
- 著名模拟鼠标点击软件小点点被收购
- mysql建表的字段类型和约束条件
- 三维模型 检索 代码_文章导读|一种基于拉普拉斯算子和联合贝叶斯模型的三维形状检索方法...
- ahk写入excel单元格_【进阶】Excel 自动化教程
- 2.3用卡诺图化简逻辑函数210807
- 二或四 通道USB数据采集卡如合通过CMI耦合,构成USB高速多通道数据采集卡
- python第三方库 invalid requirement_python第三方库安装出问题
- STM32L476入坑-3-新建工程并点亮LED灯
- 什么是CPU主频、外频、倍频?之间关系是?
- P1564 膜拜 题解
- The DELETE statement conflicted with the REFERENCE constraint
- Rancher 使用 NFS Storage Classes 进行动态 NFS 存储
- 【Windows】怎么查看CUDA版本?Conda命令安装和NVIDIA官网安装包安装的CUDA有何区别?nvcc -V和nvidia-smi获得的CUDA版本有何区别?如何指定CUDA版本?
- 2021最新Navicat15下载安装包
- Django 框架 要点
- 捷联惯导系统学习2.5(等效旋转矢量微分方程)
- 计算机培训方案范文,2015年软件培训方案模板
- 河南省安阳市谷歌高清卫星地图下载
热门文章
- openGauss Summit 2021 | 汇聚数据库创新力量 逐梦数字时代星辰大海
- 看图学习VMWare以及常见问题答疑(转)
- [990]Geohash算法原理及实现
- 网狐大联盟机器管理工具编译与使用
- Picked up _JAVA_OPTIONS: -Xmx900M”
- H3C交换机路由器配置命令大全
- 字模在c语言运行后出现乱码,C语言字模问题
- android 代码重启app
- ssh / sftp 远程登录出现:RSA host key for *.*.*.* has changed...的解决办法
- vue3解读—reactivity响应式实现