视图的返回
在前后端不分离的项目中,返回视图还是很常见的,虽然现在这样的项目不多了,但作为曾经的主流,还是有必要去了解一下的。
RequestMappingHandlerAdapter->invokeHandlerMethod():
// 这一步是绑定参数、验证参数、调用controller方法、处理返回值的过程
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
RequestMappingHandlerAdapter->getModelAndView():
// 如果存在@ResponseBody注解的情况,这边直接返回了
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
// 获取返回的视图
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
// 如果返回值不是字符串类型的
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
return mav;
视图的处理
DispatcherServlet->doDispatch():
// 如果返回为空的话,设置视图名称为请求的url
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
DispatcherServlet->processDispatchResult():
// 这里就是对应视图的呈现,其中,mv即ModelAndView对象
render(mv, request, response);
下面的resolveViewName方法中,不同的返回值对应的view类不一样,其render方法也不尽相同,下面会对常用的视图作举例,至于其他的可以自行研究
InternalResourceView
对应的是返回普通字符串或者类
DispatcherServlet->render():
......
// 获取视图名称
String viewName = mv.getViewName();
if (viewName != null) {
// 对视图名称进行解析,获取视图
// 因为返回的是字符串,所以这里的view是InternalResourceView类,它是处理JSP的
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
}
......
// InternalResourceView的render()方法是在其父类AbstractView实现的
view.render(mv.getModelInternal(), request, response);
AbstractView->render():
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
InternalResourceView->renderMergedOutputModel():
// 将model的属性放到request属性中,便于后续使用
exposeModelAsRequestAttributes(model, request);
// 获取分发的路径,需要注意的是,如果不以'/'符号开头,会截取同一路径下的
String dispatcherPath = prepareForRendering(request, response);
// 获取目标资源的RequestDispatcher(通常是JSP)
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
// 页面跳转,获取到相应的资源
rd.forward(request, response);
从代码可以看出,不同的返回类型跳转的资源路径是不一样的,假设当前请求的是/hello/test,根据不同的返回跳转资源如下。另外跳转在同一个http请求中处理,因此request和response都是同一个对象
| 返回 | 跳转资源 |
|---|---|
| c.html | /hello/c.html |
| /c.html | /c.html |
| 类类型 | /hello/hello/test |
RedirectView
对应的是返回redirect:开头的字符串,意为重定向。重定向的状态码是302,同时它会在响应头中添加Location的属性,值一般为url,最终交由调用该url的返回结果决定。可见,重定向相当于又发送了一次http请求
RedirectView->renderMergedOutputModel():
// 执行的是重定向的方法
sendRedirect(request, response, targetUrl, this.http10Compatible);
FreemarkerView
当视图名称在配置文件下有相对应的ftl文件时,使用此视图,需要添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
配置的默认属性在FreeMarkerProperties下,默认后缀名是.ftl,默认资源路径在/templates目录下
如下图,在浏览器中输入"localhost:8080/hello/test"即可渲染test.ftl,并输出到页面