我是靠谱客的博主 高贵宝贝,最近开发中收集的这篇文章主要介绍彻底搞懂spring MVC核心流程,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

spring MVC已经使我们再熟悉不过的框架了,今天从源码的角度来剖析spring MVC的启动和核心组件DispatchServlet是如何运转各个组件

一、spring MVC启动

初始化容器

 我们需要自身实现一个启动初始化类:

public class StarterInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
	
	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class[]{RootConfig.class};
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return new Class[]{WebAppConfig.class};
	}

	@Override
	protected String[] getServletMappings() {
		return new String[]{"/"};
	}
}

Tomcat在启动以后会使用SPI机制去加载SpringServletContainerInitializer类,并通过@HandlesTypes(WebApplicationInitializer.class)找到所有WebApplicationInitializer类型的类作为参数传递给SpringServletContainerInitializer#onStartup()方法,然后执行该方法。它会执行我们自定义的StarterInitializer#onStartup()方法,没有该方法就会找父类中的onStartup(),最后发现AbstractDispatcherServletInitializer#onStartup():

1、首先创建父容器(AnnotationConfigWebApplicationContext),通过自定义的getRootConfigClasses()拿到配置类,并注册到父容器中

2、通过父容器作为参数创建ContextLoaderListener监听器。并添加到servletContext(Tomcat servlet容器)

3、通过自定的getServletConfigClasses()方法拿到MVC的配置类创建子容器,并把配置类注册到子容器中

4、创建DispatcherServlet,并将DispatcherServlet添加到servletContext的Servlet容器中去

5、设置dispatcherServlet相关属性(启动时加载,设置映射路径)

Tomcat调用ContextLoaderListener#contextInitialized 进行初始化

1、context为空表示xml创建父容器,很明显在上一步我们创建了父容器

2、调用父容器的refresh()方法,加载Service和dao

3、将spring父容器设置到ServletContext的attribute属性中去。方便子容器获取父容器中的内容

Tomcat调用HttpServletBean#init()方法

1、会执行FrameworkServlet#initWebApplicationContext(),获取子容器,判断是否设置了父容器并做相应的处理

2、向子容器中注册监听器

3、调用子容器的refresh()方法,加载Controller,当执行finishRefresh()方法时,会发布一个事件,2中的监听器会执行相关事件:加载springMVC默认的相关组件,它们被配置在DispatchServlet.properties文件中,包括HandlerMapping,HandlerAdapter,ViewResolver等等为后续处理请求做准备。这里可能有问题,这些组件可能已经通过@EnableWebMvc导入DelegatingWebMvcConfiguration类加载好了

解析@RequestMapping和映射路径

这一步骤是在RequestMappingHandlerMapping初始化后调用回调方法afterPropertiesSet()执行的

1、首先拿到spring容器中所有的bean;

2、便利所有的bean,判断当前bean中是否存在@Controller和@RequestMapping注解

3、不存在,继续循环;否则,拿到当前class,遍历class内所有的方法,判断方法上是否有@RequestMapping注解,有就将注解相关属性解析为RequestMappingInfo,再判断类上是否有@RequestMapping注解,有就解析为RequestMappingInfo,并将他们合并,最后封装为map<Method, RequestMappingInfo>

4、遍历map,并解析缓存到pathLookup中<path,RequestMappingInfo>,pathLookup缓存了没有通配符的请求路径,而还有一个registry<RequestMappingInfo,MappingRegistration(包含method对象和beanName)>会存储包括通配符的路径,如果请求来了在pathLookup没有匹配到,则会去registry<>中去匹配。(通配符优先级:? > * > {} >**)

二、springMVC解析请求

当springMVC准备工作完成后,就可以开始处理请求了

1、Tomcat监听器监听到外部请求,最后交由springMVC处理,而最后真正干活的就是DispatchServlet#doDispatch()方法。

2、首先根据Tomcat包装的Request进行映射,所谓映射:解析request的请求路径,去跟pathLookup进行匹配,匹配到了就从registry拿到MappingRegistration的最终返回HandlerMethod(包括bean和method);否则直接从registry去拿到HandlerMethod(通配符优先级:? > * > {} >**)。然后将HandlerMethod封装为HandlerExecutionChain对象,并向HandlerExecutionChain对象添加程序员实现的拦截器。最终返回HandlerExecutionChain对象

3、遍历容器中所有的HandlerAdapters,用methodHandler去找到最合适的HandlerAdapter,找到即返回

4、执行拦截器的前置方法preHandle(返回false执行afterCompletion()),返回false直接return;

5、执行目标方法,返回ModelAndView;这里代码执行逻辑非常复杂,有兴趣自行研究。

6、执行拦截器的后置方法postHandle,

7、解析渲染视图,响应请求

流程图

 

遗漏responseBody分析

最后

以上就是高贵宝贝为你收集整理的彻底搞懂spring MVC核心流程的全部内容,希望文章能够帮你解决彻底搞懂spring MVC核心流程所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(58)

评论列表共有 0 条评论

立即
投稿
返回
顶部