在MVC项目搭建的过程中,我们通常会在webapp下的web.xml文件中添加上一个servelet配置,而且通常我们使用的是DispatcherServlet这个类,如下列代码所示:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath*:springmvc.xmlparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
那么我们理解一下为什么要配置DispatcherServlet呢? 在传统的Web项目中我们都是直接使用Servlet来创建接口的,为什么在SpringMVC中会使用DispatcherServlet呢?我们看一下DispatcherServlet的继承关系:
从图中可以看出,DispatcherServlet最终还是实现了HttpServlet这个类,这也就实现了Servlet的处理请求的能力。
通过前面的学习我们知道,Servlet处理请求的方法是doGet和doPost方法,那么在SpringMVC中请求的处理流程从继承关系上的实现方式如下图所示:
这样我们就能看到了,最终的请求处理是在DispatcherServlet的doDispatch方法中执行的。后续会对该方法进行详细的解析。
在DispatcherServlet中定义了九个属性,每⼀个属性都对应⼀种组件
/** MultipartResolver used by this servlet. */
// 多部件解析器
@Nullable
private MultipartResolver multipartResolver;
/** LocaleResolver used by this servlet. */
// 区域化 国际化解析器
@Nullable
private LocaleResolver localeResolver;
/** ThemeResolver used by this servlet. */
// 主题解析器
@Nullable
private ThemeResolver themeResolver;
/** List of HandlerMappings used by this servlet. */
// 处理器映射器组件
@Nullable
private List<HandlerMapping> handlerMappings;
/** List of HandlerAdapters used by this servlet. */
// 处理器适配器组件
@Nullable
private List<HandlerAdapter> handlerAdapters;
/** List of HandlerExceptionResolvers used by this servlet. */
// 异常解析器组件
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** RequestToViewNameTranslator used by this servlet. */
// 默认视图名转换器组件
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
/** FlashMapManager used by this servlet. */
// flash属性管理组件
@Nullable
private FlashMapManager flashMapManager;
/** List of ViewResolvers used by this servlet. */
// 视图解析器
@Nullable
private List<ViewResolver> viewResolvers;
九⼤组件都是定义了接⼝,接⼝其实就是定义了该组件的规范,⽐如ViewResolver、 HandlerAdapter等都是接⼝。
SpringMVC是在Spring框架的基础上进行构建的,在DispatcherServlet类中也实现了这个onRefresh方法,实现九大组件的初始化:
/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
// 初始化多部件解析去
initMultipartResolver(context);
// 初始化国际化解析器
initLocaleResolver(context);
// 初始化主题解析器
initThemeResolver(context);
// 初始化处理器映射器
initHandlerMappings(context);
// 初始化处理器适配器
initHandlerAdapters(context);
// 初始化异常解析器
initHandlerExceptionResolvers(context);
// 初始化视图转换器
initRequestToViewNameTranslator(context);
// 初始化视图解析器
initViewResolvers(context);
// 初始化flash管理器
initFlashMapManager(context);
}
下面重点分析initHandlerMappings(context)
2. initHandlerMappings(context)方法解析
/**
* Initialize the HandlerMappings used by this class.
* If no HandlerMapping beans are defined in the BeanFactory for this namespace,
* we default to BeanNameUrlHandlerMapping.
*/
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 按照HandlerMapping.class去IOC容器中找到所有的HandlerMapping
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
// 否则在IOC容器按照固定的名称handlerMapping查询
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
// 最后还为空,则按照默认策略生成
// 按照默认方式生成HandlerMapping
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
如果按照类型和固定的名称在IOC容器找不到对应的组件,则会按照默认的策略去生产HandlerMapping组件,默认的策略是在DispatcherServlet.properties中进行配置的,
下面是DispatcherServlet.properties中的配置:
我们可以看到,这里一共对九大组件中的八个进行了默认的配置,这也说明处理多部件解析器外,其他组件的初始化流程其实跟HandlerMapping处理流程一直唯一没有进行默认配置的就是MultipartResolver-多部件解析器,这也是要注意的地方:
/**
* Initialize the MultipartResolver used by this class.
* If no bean is defined with the given name in the BeanFactory for this namespace,
* no multipart handling is provided.
*/
private void initMultipartResolver(ApplicationContext context) {
try {
// 按照默认名称multipartResolver去IOC查询实例
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.multipartResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
this.multipartResolver = null;
if (logger.isTraceEnabled()) {
logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
}
}
}
多部件解析器的初始化必须按照id注册对象(multipartResolver)
可以看到是在DispatcherServlet类中的doDispatch方法中执行的请求。
2. 页面渲染时机
1.流程概览
SpringMVC处理请求的流程即为org.springframework.web.servlet.DispatcherServlet#doDispatch⽅法的执⾏过程
(1)调⽤getHandler()获取到能够处理当前请求的执⾏链 HandlerExecutionChain(Handler+拦截器)。但是如何去getHandler的?后⾯进⾏分析…
(2)调⽤getHandlerAdapter();获取能够执⾏1)中Handler的适配器,但是如何去getHandlerAdapter的?后⾯进⾏分析…
(3)适配器调⽤Handler执⾏ha.handle(总会返回⼀个ModelAndView对象)
(4)调⽤processDispatchResult()⽅法完成视图渲染跳转
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
/**
* 1. 检查是否有文件上传
*/
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
/**
* 2. 取得处理当前请求的Controller,这⾥也称为Handler,即处理器
* 这⾥并不是直接返回 Controller,⽽是返回 HandlerExecutionChain 请求处
* 理链对象
* 该对象封装了Handler和Inteceptor
*/
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果handler为空,则返回404
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
/**
* 3. 获取处理请求的处理器适配器 HandlerAdapter
*/
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 处理 last-modified 请求头
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
/**
* 4. 实际处理器处理请求,返回结果视图对象
*/
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 结果视图对象的处理
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
/**
* 5. 跳转⻚⾯,渲染视图
*/
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
// 最终会调⽤HandlerInterceptor的afterCompletion ⽅法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
// 最终会调⽤HandlerInterceptor的afterCompletion ⽅法
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
遍历两个HandlerMapping,试图获取能够处理当前请求的执⾏链
3.核⼼步骤getHandlerAdapter⽅法剖析
遍历各个HandlerAdapter,看哪个Adapter⽀持处理当前Handler
4.核心:ha.handle方法剖析
时机点:
从断点进入:
进入invokeHandlerMethod方法:
进入invokeAndHandle方法:
进入invokeForRequest方法:
5.核⼼步骤processDispatchResult⽅法剖析
关键的时机点: render方法-完成视图渲染
进入render方法中,
解析视图名称,进入到resolveViewName方法中:
每个视图解析都去解析视图,得到一个视图,进入到viewResolver.resolveViewName方法中:
创建视图,视频名称为"success",进入到createView方法中:
调用父类的方法,创建视图,点击进入:
构建视图,点击进入:
到这里,逻辑的视图跟物理视图就对应上了,可以作为一个视图返回了,视图构建成功后,下面的方法进行参数设置:
进入view.render方法:
进入exposeModelAsRequestAttributes方法: