博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
DispatcherServlet的九大组件——ViewResolver组件——万字长文
阅读量:3960 次
发布时间:2019-05-24

本文共 18656 字,大约阅读时间需要 62 分钟。

文章目录

1:为什么要学习DispatcherServlet的九大组件

在如下的这篇文章中

已经探讨过了,为什么一个请求最终会到DispatcherServlet中的doDispatch方法中实现和该方法中的执行可以分为11个逻辑代码块。那为什么今天要聊这个话题呢?

这些组件对我们今后的开发是否有帮助呢?以我之愚见,我觉得是有的,举一个例子:九大组件之中的处理器异常解析器组件,如果我们去探究HandlerExceptionResolver 的源码,在以后的开发中我们就可以自定义实现 HandlerExceptionResolver 处理异常(可以作为默认的全局异常处理规则)等等。

那这个九大组件和doDispatch方法又有什么关系呢?在上面的文章中的末尾我们已经提到

doDispatch方法中出现了很多组件,这些组件都是Spring MVC整个处理过程中不可获缺的部分。正是通过这些组件之间的搭配组合,才令整个Spring MVC框架完整地运行起来,使用框架进行开发时,各种便捷功能都是通过这些组件辅助完成的。这就像盖房子一样,通过一个个小组件,最终搭建起一个完整的艺术品。

在接下来的三章中,会直接切入正题带大家了解视图解析器的查找过程和视图执行渲染的过程,然后走一遍完整的视图解析过程,最后带大家总结SpringMVC对视图的支持。

在这里插入图片描述
在这先强调,下面出现的resolveViewName方法是视图解析器实现类中的方法,我们通过打断点进行调试会发现,它出现在DispatcherServlet类中的doDispatch方法中的processDispatchResult方法中的render方法中的resolveViewName方法中,在DispatcherServlet类的resolveViewName方法中会进行视图解析(就是调用视图解析器实现类中的resolveViewName方法),如上过程要通过大家调试源码一步一步进行实现,在上面的文章4.5中有详细介绍。

这里就给大家展示一下DispatcherServlet类的resolveViewName方法的逻辑代码

在这里插入图片描述

3:视图解析器的查找过程

在大多数情况下,开发者返回的视图都是视图名的形式 ,视图解析器就是用来把视图名解析为视图数据的组件。其中仅包含一个方法,即View resolveViewName(String viewName,Locale locale):通过视图名与Locale区域信息解析为视图类型。

在DispatcherServlet中该组件为列表,使用时同样会遍历组件列表查找到第一 个返回不为空的View作为最终的View使用。在只添加Thymeleaf模板引擎依赖的情况下,该组件列表包含五个组件,按顺序如下。

  1. ContentNegotiatingViewResolver:整合全部视图解析器,附加内容类型协商逻辑。

  2. BeanNameViewResolver:根据视图名获取Bean,把Bean作为View返回。

  3. ThymeleafViewResolver:Thymeleaf 模板视图解析器。

  4. ViewResolverComposite:组合视图解析器类,用于组合多个视图解析器组件,内部维护视图解析器列表,执行解析时遍历内部视图解析器列表进行解析。内部视图解析器列表默认为空。

  5. InternalResourceView:内部资源视图解析器, 用于解析内部的些视图资源,如jsp等视图。

    上面的一段话的意思,如果放入到源码中就会很好理解。如下
    在这里插入图片描述

视图解析器返回值为View视图类型,其为视图的封装,主要作用是根据Model模型及请求与响应,对结果进行渲染, 把渲染后的结果写入响应体中,其提供的方法如下。

default Sring getConentType():返回视图的ContentType用于请求头 Acpetp做判断,以确定此视图是否支持Accpet中的任意一种MediaType。默认为null, 表示无显式声明类型。

void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServlet-Response response):根据Model模型对视图进行渲染,并把渲染内容写入响应体。

上面说到的视图会在第4章和第6.1节详细讲到,这里不在介绍

下面分别来了解一下ViewResolver组件中的五大视图解析器。

3.1 内容协商视图解析器

ContentNegotiatingViewResolver包含内容协商的逻辑,在通过视图名无法直接获取视图时,可根据请求头中声明的可接收内容类型,解析内容类型对应的文件扩展名,并自动为视图名添加文件扩展名再次解析。

ContentNegotiatingViewResolver类下的resolveViewName方法的逻辑代码如下:

@Override	@Nullable	public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes(); Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");//根据请求头Accept获取请求的MediaTypes列表 List
requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());//如果请求的MediaTypes不为空 if (requestedMediaTypes != null) {
//根据视图名和请求MediaType获取全部视图列表 List
candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);//在有多个视图返回是,根据固定策略获取最佳匹配视图 View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);//不为空则返回 if (bestView != null) {
return bestView; } }//如果没有找到,则视图用于响应写入406状态码 String mediaTypeInfo = logger.isDebugEnabled() && requestedMediaTypes != null ? " given " + requestedMediaTypes.toString() : ""; if (this.useNotAcceptableStatusCode) {
if (logger.isDebugEnabled()) {
logger.debug("Using 406 NOT_ACCEPTABLE" + mediaTypeInfo); } return NOT_ACCEPTABLE_VIEW; }//否则放回null,通过下一个视图解析器进行解析 else {
logger.debug("View remains unresolved" + mediaTypeInfo); return null; } }

我们可以通过断点调试,在如下处打一个断点,然后debug启动,进入到该断点所在处的方法中执行。来演示上述执行过程

在这里插入图片描述
在这里插入图片描述
最终返回的View类型依赖于从候选列表里获取的视图类型。获取候选视图列表与选择最佳视图的方法的代码如下一一介绍:

获取候选视图列表代码逻辑如下

private List
getCandidateViews(String viewName, Locale locale, List
requestedMediaTypes) throws Exception {
//用于保存匹配的视图候选列表 List
candidateViews = new ArrayList<>(); if (this.viewResolvers != null) {
Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set");//遍历全部视图解析器 for (ViewResolver viewResolver : this.viewResolvers) {
//尝试直接获取视图 View view = viewResolver.resolveViewName(viewName, locale);//如果直接获取视图不为空,添加到候选列表 if (view != null) {
candidateViews.add(view); }//遍历请求中全部Accept头中的MediaType for (MediaType requestedMediaType : requestedMediaTypes) {
// 通过内容协商器, 根据MediaType获取该MediaType对应的文件扩展名列表 List
extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);//遍历全部扩展名 for (String extension : extensions) {
String viewNameWithExtension = viewName + '.' + extension;//再次尝试获取视图 view = viewResolver.resolveViewName(viewNameWithExtension, locale); if (view != null) {
candidateViews.add(view); } } } } } if (!CollectionUtils.isEmpty(this.defaultViews)) {
candidateViews.addAll(this.defaultViews); }//返回候选列表,上一级逻辑中再通过视图的ContentType与Accept接收的MediaType做对比返回最优视图 return candidateViews; }

我们可以通过断点调试,在如下处打一个断点,然后debug启动,进入到该断点所在处的方法中执行。来演示上述执行过程,调试完成结果如下

在这里插入图片描述
在这里插入图片描述
选择最佳视图的方法的代码逻辑如下

//获取最后视图逻辑	@Nullable	private View getBestView(List
candidateViews, List
requestedMediaTypes, RequestAttributes attrs) {
//遍历全部候选视图 for (View candidateView : candidateViews) {
//如果视图类型是SmartView if (candidateView instanceof SmartView) {
SmartView smartView = (SmartView) candidateView;//且视图是重定向视图,则直接返回此视图,以重定向为准 if (smartView.isRedirectView()) {
return candidateView; } } }//遍历请求头Accept可接受MediaType列表 for (MediaType mediaType : requestedMediaTypes) {
//遍历候选视图列表 for (View candidateView : candidateViews) {
if (StringUtils.hasText(candidateView.getContentType())) {
MediaType candidateContentType = MediaType.parseMediaType(candidateView.getContentType()); if (mediaType.isCompatibleWith(candidateContentType)) {
//,,,, attrs.setAttribute(View.SELECTED_CONTENT_TYPE, mediaType, RequestAttributes.SCOPE_REQUEST); return candidateView; } } } } return null; }

如上进入到getBestView方法中进行调试,调试完成结果如下

在这里插入图片描述

内容协商视图解析器类也相当于对原始的视图解析器的包装类,另外添加自动根据请求类型进行协商的逻辑,保证返回视图的ContentType支持请求的Accept 头中的某个MediaType,以保证返回数据的可读性。返回视图的具体类型依赖于内部封装的视图解析器列表返回的视图类型。

3.2 BeanName视图解析器

BeanName视图解析器与之前提到的BeanName处理器映射功能类似,BeanName 视图解析器是把视图名作为BeanName名,根据该BeanName获取应用上下文中对应的Bean,把这个Bean直接作为返回的视图使用。其逻辑代码如下:

@Override	@Nullable	public View resolveViewName(String viewName, Locale locale) throws BeansException {
//获取应用上下文 ApplicationContext context = obtainApplicationContext();//如果应用上下文中不包含视图名的Bean,则返回空 if (!context.containsBean(viewName)) {
return null; }//如果该Bean的类型不是View类型,则同样不能返回,该方法只能返回View类型 if (!context.isTypeMatch(viewName, View.class)) {
if (logger.isDebugEnabled()) {
logger.debug("Found bean named '" + viewName + "' but it does not implement View"); } return null; }//返回该Bean,作为View使用 return context.getBean(viewName, View.class); }

返回具体的视图类型依赖于注册到应用上下文中的Bean类型。

这种解析器的逻辑相对来说比较简单,但实际场景中用处比较小,因为要求开发者提供View类型的Bean,不如使用模版引擎的视图方便

3.3 Thymeleaf视图解析器

在项目中添加Thymeleaf模板引擎的依赖后,会自动添加Thymeleaf视图解析器,该视图解析器用于对Thymeleaf模板视图进行解析,其中处理逻辑如下。该视图解析器的rsolveViewName方法实现在其父类AbstractCachingViewResolver中,该父类通过名称可以判断,为模板解析添加缓存的抽象类。在某些视图解析器中,解析过程可能涉及多次磁盘IO, 性能损耗较大,故为其视图名与返回的视图添加缓存是很常见的一种优化性能的方法,其方法如下:

AbstractCachingViewResolver类下的resolveViewName方法的代码逻辑

@Override	@Nullable	public View resolveViewName(String viewName, Locale locale) throws Exception {
//如果配置了不缓存 if (!isCache()) {
//则直接调用创建视图逻辑,返回创建的视图实例 return createView(viewName, locale); } else {
//根据该方法参数返回一个缓存Key,一般为viewName_ locale, 在忽略locale的实现中,直接返回viewName Object cacheKey = getCacheKey(viewName, locale);//viewAccessCache是ConcurrentHashMap,保存访问过的视图缓存,尝试根据key从访问过的视图缓存中获取视图 // private final Map
viewAccessCache = new ConcurrentHashMap<>(DEFAULT_CACHE_LIMIT); View view = this.viewAccessCache.get(cacheKey); if (view == null) {
synchronized (this.viewCreationCache) {
view = this.viewCreationCache.get(cacheKey); if (view == null) {
// Ask the subclass to create the View object. view = createView(viewName, locale); if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW; }//创建后视图不为空 if (view != null) {
//写入访问缓存 this.viewAccessCache.put(cacheKey, view);//写入创建缓存 this.viewCreationCache.put(cacheKey, view); } } } } else {
if (logger.isTraceEnabled()) {
logger.trace(formatKey(cacheKey) + "served from cache"); } } return (view != UNRESOLVED_VIEW ? view : null); } }

在AbstractCachingViewResolver创建视图createView的逻辑中,直接调用本类的loadView方法,该方法是抽象方法,子类需要实现这个方法。

AbstractCachingViewResolver类下的createView方法和抽象的loadView方法

@Nullable	protected View createView(String viewName, Locale locale) throws Exception {
return loadView(viewName, locale); } @Nullable protected abstract View loadView(String viewName, Locale locale) throws Exception;

在ThymeleafViewResolver中还重写了createView方法。在该方法中,执行一些内置的视图前缀判断,以进行不同的处理,代码如下:

ThymeleafViewResolver类中的createView方法逻辑如下

@Override    protected View createView(final String viewName, final Locale locale) throws Exception {
//alwaysProcessRedirectAndForward标记是否处理redirect和forword为前缀的视图名,默认为true//canHandle判断是否可被此视图解析器处理 if (!this.alwaysProcessRedirectAndForward && !canHandle(viewName, locale)) {
//.. return null; }//如果视图名以redirect:为前缀,则视为重定向视图 if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
//.. //截取重定向地址 final String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length(), viewName.length()); //创建一个重定向视图,传入相关初始化参数 final RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());//通过应用上下文初始化此视图,用以把该视图需要的应用上下文中信息初始化到实例中 return (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName); } //处理forward:前缀的视图请求 if (viewName.startsWith(FORWARD_URL_PREFIX)) {
//.. final String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length(), viewName.length()); return new InternalResourceView(forwardUrl); }//处理后执行逻辑canHandle,判断此解析器是否支持该视图名,不支持则返回空 if (this.alwaysProcessRedirectAndForward && !canHandle(viewName, locale)) {
//.. return null; } //... // 执行加载视图的方法 return loadView(viewName, locale); }

在ceateView的逻辑中,对视图名做了些特殊处理, 核心创建视图方法还是 loadView方法。在该方法中,包括尝试从应用上下文获取该视图名对应的Bean、创建ThymeleafView视图类实例、通过应用上下文初始化ThymeleafView实例、使用应用的一些配置 设置视图的属性等逻辑

同时,在此处返回的视图ThymeleafView 中,其渲染逻辑是先通过视图名及应用配置的视图前缀和后缀,拼接出视图文件路径,再获取视图文件流,把应用上下文、传入的模型参数、Servlet 上下文等信息作为模板信息,通过模板引擎逻辑渲染视图文件流,并把值染后的数据流写入Http响应体中。内容较多,且与模板引擎关联较多,此处不再赘述。

3.4 组合视图解析器

组合视图解析器ViewResolverComposite 的功能与内容协商视图解析器ContentNegotiatingViewResolver的功能类似,但组合视图解析器只是简单的组合多个视图解析器,并不包含内容协商相关逻辑,其实现也非常简单,

ViewResolverComposite 类中的resolveViewName方法代码如下:

@Override	@Nullable	public View resolveViewName(String viewName, Locale locale) throws Exception {
//遍历本类包装的视图解析器列表 for (ViewResolver viewResolver : this.viewResolvers) {
//第一个获取非空View的视图解析器生效,返回该View View view = viewResolver.resolveViewName(viewName, locale); if (view != null) {
return view; } } return null; }

说明:在默认情况下该观图解析器中包装的视图解析器列表为空。

3.5 内部资源视图解析器

内部资源视图解析器(IntemalResoureViewResolver)用于解析一些内 部资源视图,如重定向视图、转发视图或Servlet 内置支持的JSP 视图等。

由于作者水平有限,这里不在进行详细介绍

4:视图的渲染过程(视图的实现)

本节主要讲解第3章提到的视图实现,主要关注两种视图,即RedirectView与InternalResourceView,其内部实现如下图所示

在这里插入图片描述
这两种视图都继承自AbstractUrlBasedView,该抽象视图用于封装基于URL的视图的统一逻辑,在AbstractView中也封装了与所有视图处理相关的抽象逻辑。

对于视图最重要的render方法,在AbstractView中有实现,

AbstractView类下的render方法代码如下

@Override	public void render(@Nullable Map
model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//... //用于把参数的模型 与内置模型属性合并,合并顺序为:本视图的所有静态属性、当前请求的路径,传入的模型参数及封装好的请清求上下文//请求上下文中包含了主题源与信息源的相关内容//暴露的这些属性可以在后续处理中使用,以实现各种功能 Map
mergedModel = createMergedOutputModel(model, request, response);//用于准备响应,如添加一些公共响应头等 prepareResponse(request, response);//执行抽象方法,把合并后的模型作为参数使用,渲染视图 renderMergedOutputModel(mergedModel, getRequestToExpose(request), response); }
protected abstract void renderMergedOutputModel(			Map
model, HttpServletRequest request, HttpServletResponse response) throws Exception;

在RedirectView和InternalResourceView中,有该方法(renderMergedOutputModel)的不同实现,RedirectView视图中实现代码如下:

@Override	protected void renderMergedOutputModel(Map
model, HttpServletRequest request, HttpServletResponse response) throws IOException {
//基于模板参数处理,实现了支持重定向模板中包含了模板参数功能 String targetUrl = createTargetUrl(model, request);//更新目标地址,使用组件RequestDataValueProcessor执行处理逻辑,如果应用上下文中不存在该组件,则不执行处理,使用该组件可对重定向地址进行额外的处理 targetUrl = updateTargetUrl(targetUrl, model, request, response);//触发重定向,通过FlashMapManager保存当前请求输出的FlashMap到Session中 RequestContextUtils.saveOutputFlashMap(targetUrl, request, response);//发送重定向,向响应写入重定向状态码,并添加location响应头为重定向目标地址 sendRedirect(request, response, targetUrl, this.http10Compatible); }

该视图逻辑很清晰,对应用中返回的视图名进行额外的处理。作为重定向的location直接返回重定向响应。同时该视图实现SmartView视图接口,用于标记自己为重定向视图,以供对视图统一处理时进行判断。这也符合重定向的逻辑,

对InternalResourceView内部资源视图,我们不做过多介绍。

重定向视图直接向响应写入重定向相关参数,内部资源视图则交给Servlet 框架自身处理,获取内部相关资源。

除此之外,还有各种模板引擎构造的视图,这些逻辑与模板引擎高度相关,其内部逻辑是通过配置的模板文件前缀加视图名加模板文件后缀拼接后查找对应位置的模板文文件,之后使用模板引擎的渲染功能对此模板进行渲染。

5:视图解析过程源码调试

我们用如下代码,进行调试

@PostMapping("/loginadmin")    public String login(RedirectAttributes redirectAttributes,HttpSession session, String loginUser, String password,Map
map,NewsInformation newsInformation){
//此处省略登录逻辑 return "redirect:/main.html"; }

在这里可以先明确告诉大家,这一个过程,我们使用内容协商视图解析器来查找视图和使用重定向视图来执行渲染功能。步骤如下

步骤1:准备工作

在这里插入图片描述
在这里插入图片描述
步骤2:进入resolveViewName方法中,探究视图解析器解析视图的流程
在这里插入图片描述
在这里插入图片描述

步骤3:进入render方法中,探究视图是如何进行渲染的

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
步骤4:结束
在这里插入图片描述
成功跳转到如下界面
在这里插入图片描述

在下面,我们介绍视图类和视图解析器过程中,尽量会找到对应的示例,来演示视图查找和视图渲染都是用到什么视图解析器和视图类

在这里插入图片描述

6:SpringMVC对视图的支持

上面所讲的一切,归功到底都是SpringMVC对视图的支持非常丰富,包括各种视图的模板引擎和内置的特殊视图。同时还提供了视图自动查找能力,根据视图名通过视图解析器解析出对应的视图,这和控制器中处理器的查找和执行分离是相同的,这里分离了视图的查找( view = resolveViewName(viewName, mv.getModelInternal(), locale, request);

)和渲染( view.render(mv.getModelInternal(), request, response);)。这些步骤都是在控制器的最后渲染步骤(DispatcherServlet类中的render方法)中执行的。我们先来了解一下常见的视图类型

6.1 视图类型

Spring MVC通过接口org. sringframework web.servet.View封装了所有视图的实现,通过该接口的render 方法,依据传入的Map类型的model, 对视图文件进行渲染并写入Response来完成处理过程

控制器只负责调用视图的渲染方法,将视图与Model进行绑定而视图的渲染则是由视图的实现来执行的,这种分离接口和实现的方式在面向对象中也是很好的解耦设计。 把上面的一段话,放到DispatcherServlet类中的render方法中,就变得好理解起来

在这里插入图片描述

接下来看看视图都有哪些常用实现。

6.1.1 内部资源视图(InternalResourceView)

该视图代表内部的资源。创建该视图时,需要传入URL,代表内部资源的路径,而在该视图渲染时,会进行请求转发。通过调用请求的getRequestDispatcher 方法,传入URL 参数,拿到内部的RequestDispatcher后转发这个请求。

该类型的使用场景常见的有3种。

  • 访问静态资源:这种场景一般针对一些静态资源, 例如要通过控制器根据逻辑分到不同的静态资源时,一般可以使用这种视图。这种视图一般结合SimpletUrlHandlerMapping使用,视图名是静态资源路径,处理器一般是HttpRequestHandler,直接把资源文件数据通过流写入HTTP响应中。

  • 访问JSP视图:当使用外部Tomcat容器启动时,默认支持JSP视图,对于JSP的支持方式就是通过内部资源视图实现的。但是在SpringBoot中,因为使用了内容Tomcat容器,故对JSP的支持并不充分。

  • 转发视图:Spring MVC提供了转发视图,通过为视图名添加forward:前缀,可以在服务器内部进行一次转发,转发路径是视图名去掉forward:前缀。转发无需浏览器重新请求,中间请求的跳转是完全通过服务器内部进行的,所以浏览器上面的URL并不会改变。(如果使用重定向(redirect)就会改变,这也是forword和redirect的区别之一)

因为这3种的实现原理都是通过获取对应路径的RequestDispatcher实现的,故使用内部资源视图可以实现这3个功能。

在转发视图中,转发前模型中的属性通过request.setAttribute 方法添加到request的属性中,若要在转发的目标中使用,则需要通过request.getAttribute 获取。因为在转发过程中request是不会变的,所以可以通过request 进行属性共享。(如果使用重定向(redirect),该request会发生改变,这也是forword和redirect的区别之一)

接下来,我们看几个演示,来看什么情况下会用到内部资源视图类(InternalResourceView)

演示1:访问jsp视图的时候,并且该演示是在使用外部Tomcat容器启动的
前端代码

处理器方法返回string表示视图名称

姓名:
年龄:

后端代码

@RequestMapping(value = "/returnstring1.do")    public String receive1(String name, String age, Map
map){
//doGet //调用service,获取结果数据(student) Student student = new Student(); student.setAge("12"); student.setName("zl"); map.put("student",student); return "show"; }

上面代码不在做一一介绍,return “show” 该show是jsp页面

代码跳到关键点就可以看到,返回的视图类就是InternalResourceView类
在这里插入图片描述
演示2:进行转发视图的时候,并且该演示是在使用外部Tomcat容器启动的

后端

@RequestMapping(value = "/forwardview.do",method = RequestMethod.GET)    public String forwardview(Model model){
//doGet model.addAttribute("info","转发前属性"); return "forward:forwardTargetView"; } @RequestMapping(value = "/forwardTargetView.do",method = RequestMethod.GET) public String forwardTargetView(Model model){
//doGet Object info = model.getAttribute("info"); model.addAttribute("first",info); model.addAttribute("second","转发后属性"); return "other"; }

前端

first:${first}

second:${second}

上面代码不在做一一介绍,return “other” 该other是jsp页面

代码跳到关键点就可以看到,返回的视图类就是InternalResourceView类
在这里插入图片描述
如上的两个演示代码,如果继续调试,最终都会交由InternalResourceView类中的render方法进行渲染

6.1.2 重定向视图(RedirectView)

上面内部视图为请求转发提供了支持,除了转发,视图还有重定向的概念。重定向是通过向浏览器返回状态码 302,并设置响应头中的Location 为需要重定向的地址实现的。

类似于转发请求,重定向的视图只需要在视图名前添加redirect前缀即可,此时转发视图会自动处理视图名为URL视图,并对HTTP的响应设置状态码为302,响应头Location 根据redirect:后面的名称取到的地址。

如果大家对上面的两段话不理解,可以参考如下文章,有做详细介绍,在此不在赘述

第五章的源码调试,就是使用到了重定向视图,在此我们在举一个例子来进行调试

@RequestMapping(value = "/redirect.do",method = RequestMethod.GET)    public String redirect1(RedirectAttributes model){
//doGet model.addFlashAttribute("first","重定向前属性"); return "redirect:redirecttarget.do"; } @RequestMapping(value = "/redirecttarget.do",method = RequestMethod.GET) public String redirect2(RedirectAttributes model){
//doGet model.addFlashAttribute("second","重定向后属性"); return "other"; }

代码跳到关键点就可以看到,返回的视图类就是RedirectView类

在这里插入图片描述

6.1.2 模板引擎视图(ThymeleafView)

模板引擎视图是使用模板引擎实现的视图类型,模板引擎本身的设计目的就是为了使页面的静态内容与页面的数据动态内容分离,即为了实现视图的功能而设计的。Spring MVC提供了强大的扩展功能,我们可以把任意模板引擎嵌入到Spring MVC的视图解析中,而不影响任何其他功能,这也体现了MVC分离的优势。

在Spring Boot中,官方推荐使用Thymeleaf作为默认的模板引擎,用于视图解析。其返回的视图类型是ThymeleafView。

Thymeleaf语法的最大特点是使用自然的HTML标记语言,在无数据展示时,仍然可保持页面的完整性,其模板自身就是原始的页面。Model 数据绑定均使用HTML中的标签屋性,同时使用独立的命名空间,使得未渲染的页面也可在浏览器中打开并保持设计时的样式。

接下来,我们看几个演示,来看什么情况下会用到模板引擎视图(ThymeleafView)

鄙人认为现在如果是开发SSM项目(前后端粘合通过thymeleaf),如果你进行代码调试查找springmvc是如何进行视图查找和进行视图渲染的,我们一般都是使用模板引擎视图来执行视图渲染的,而视图解析都是通过内容协商视图解析器来查找到模板引擎视图。

如下是我的一个SSM项目中,在进行追源码的时候,调试的结果,大家会发现在执行如下方法,返回的视图几乎都是模板引擎视图类来实现的

@Nullable	protected View resolveViewName(String viewName, @Nullable Map
model, Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
//ThymeleafView View view = viewResolver.resolveViewName(viewName, locale); if (view != null) {
return view; } } } return null; }

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.2 视图查找解析

在6.1节分析了各种不同类型的视图支持,但是在使用视图时,大多数情况下,只用声明视图名即可查找到对应的视图,就如在@RequestMapping 方法中返回视图名一样。SpringMVC对这种使用视图名查找视图又提供什么样的支持呢。下面就来看一下视图的查找解析策略。

Spring MVC提供了ViewResolver 接口用以通过视图名查找并解析视图。该接口有resolveViewName方法,返回View视图。接受String类型的视图名,以该视图名进行查找,还有Locale 类型的地区参数,用于进行国际化。 Spring MVC的控制器中支持多个ViewResolver,查找视图时进行遍历查找,直到找到相应的视图,如果没有找到则报错提示没有查找到相应的视图。

上面的一段话,放到代码中就变得非常好理解

在这里插入图片描述

默认情况下Spring MVC支持五种查找与解析的策略。在本章第3章已经详细介绍,在此不在赘述

转载地址:http://xrozi.baihongyu.com/

你可能感兴趣的文章
用例图(UseCase Diagram)—UML图(一)
查看>>
类图(Class diagram)—UML图(二)
查看>>
对象图(Object Diagram)—UML图(三)
查看>>
活动图(Activity Diagram)—UML图(四)
查看>>
状态图(Statechart Diagram)—UML图(五)
查看>>
时序图(Sequence Diagram)—UML图(六)
查看>>
构件图(Component Diagram)—UML图(八)
查看>>
部署图(Deployment Diagram)—UML图(九)
查看>>
协作图(Collaboration Diagram)—UML图(七)
查看>>
什么是RUP
查看>>
什么是UML(UML总结)
查看>>
UML基础与应用系列文章汇总
查看>>
C#方法重载(overload)方法重写(override)隐藏(new)
查看>>
javascript实现滚动图片
查看>>
css+div练手-工作室
查看>>
CSS+DIV布局之道
查看>>
CSS+DIV练手-公司
查看>>
CSS+DIV练手—鲜花展
查看>>
深入浅出JavaScript(1)—ECMAScript
查看>>
深入浅出JavaScript(2)—ECMAScript
查看>>