SpringMVC工作流程
博客地址:SpringMVC工作流程
SpringMVC的核心在于它的流程,所以了解SpringMVC的工作流程对于我们使用SpringMVC非常重要,SpringMVC是一种基于Servlet的技术,它提供了核心控制器DispatcherServlet和相关组件。其流程图:
1.首先用户发送一个请求到服务器,在使用springMVC之前,我们应该在web.xml对DispatcherServlet进行配置过了:
<!--使用Spring MVC,配置DispatcherServlet是第一步。DispatcherServlet是一个Servlet,,所以可以配置多个DispatcherServlet-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--指明了配置文件的文件名,不使用默认配置文件名,而使用dispatcher-servlet.xml配置文件。-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/dispatcher-servlet.xml</param-value>
</init-param>
<!--是启动顺序,让这个Servlet随Servletp容器一起启动。-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!--会拦截URL中带“/”的请求。-->
<url-pattern>/</url-pattern>
</servlet-mapping>
复制代码
如果使用springBoot,那么springBoot会帮我们注册一个默认的DispatcherServlet,而如果该请求与配置中的url-pattern匹配的话,该请求就会交由该DispatcherServlet来进行处理 2.DispatcherServlet根据url,遍历HandlerMappings,获得由处理器和拦截器组成的执行链返回给DispatcherServlet, 其源码如下,遍历HandlerMappings(初始化时获得),如果当中有一个HandlerMapping调用getHandler方法时返回值不为null,那么getHandler就会返回一个由拦截器和处理器组成的HandlerExecutionChain执行链。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
复制代码
3.将获得的执行链返回给DispatcherServlet 4.根据获得的Handler调用HandlerAdapter,其实现也是类似,遍历HandlerAdapters,如果类型匹配则返回该HandlerAdapter,源码如下:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
复制代码
注意:此时会依次执行拦截器的preHandle方法,如果遇到一个拦截器return false就会停止 5.提取request中的数据模型,进行数据转换,数据格式化,数据验证等操作,然后执行Handler,调用具体的方法,其实现如下,其中handleInternal由具体的Adapter,比如@RequestMapping注解的handler会由RequestMappingHandlerAdapter来处理。这里就不继续分析源码了,想看源码详细分析的话这个大兄弟写得很好SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解:
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
private int order = Ordered.LOWEST_PRECEDENCE;
@Override
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
//抽象方法,由具体的Adapter实现
protected abstract ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
}
复制代码
6.handleInternal的具体实现会执行一个invokeHandlerMethod ,执行后会返回一个ModelAndView视图,源码:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
.........
//执行Controller中的RequestMapping注释的方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
//返回ModelAndView视图
return getModelAndView(mavContainer, modelFactory, webRequest);
}
复制代码
7.HandleAdapter返回ModelAndView给DispatcherServlet(注意:在下一步渲染视图之前会执行postHandle方法) 8~9.解析视图和渲染视图都是在render方法里面执行的, render方法如下:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}
复制代码
其中,
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
复制代码
这个方法会遍历DispatcherServlet中的ViewResolver集合并返回对应的视图,对应流程图中的第八步和第九步 10.上面的render方法中的:
view.render(mv.getModelInternal(), request, response);
复制代码
则是渲染视图的方法(渲染视图后会执行拦截器的afterCompletion方法),其实现类源码如下:
public abstract class
AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware {
... ...
@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (logger.isTraceEnabled()) {
logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
" and static attributes " + this.staticAttributes);
}
// 创建整合后需要返回给浏览器的Model
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
// 设置response 报文头
prepareResponse(request, response);
// 渲染数据,通过模板方法由子类实现,如InternalResourceView
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
... ...
}
复制代码
11.上面view.render方法的最后一步:
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
复制代码
其源码如下:
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 将model中的数据设置到request
exposeModelAsRequestAttributes(model, request);
// 本类中的此函数是空函数,留给子类比如JstlView去实现自定义逻辑
exposeHelpers(request);
// 跳转目的页面路径
String dispatcherPath = prepareForRendering(request, response);
// 获取跳转控制器RequestDispatcher
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// 直接返回用户资源
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.include(request, response);
}
// 携带request和response跳转到另一个控制器方法
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.forward(request, response);
}
}
复制代码
可以看到,这个方法完成了最后把视图封装好和把结果返回给用户的操作
参考:DispatcherServlet来看spring mvc请求处理过程 SpringMVC之源码分析--View(一)