首页 > 其他分享 >SpringMvc 视图解析

SpringMvc 视图解析

时间:2023-04-20 22:38:49浏览次数:42  
标签:解析器 return SpringMvc request 视图 View 解析 view

重定向

forward前缀

若要返回/WEB-INF/pages/success.jsp,则直接return "success";即可。
若要返回webapp下的helloworld.jsp页面:

相对路径 ../../hello,需return "../../helloworld";
forward前缀,转发一个页面,不会进行拼串。需return "forward:/helloworld.jsp";
格式:  forward:转发的路径

    @RequestMapping("/hello")
    public String hello(){
        return "forward:/helloworld.jsp";
    }
    @RequestMapping("/hello1")
    public String hello1(){
        return "forward:/hello";
    }

redirect前缀

重定向 redirect:重定向的路径 视图解析器不会进行拼串
原生的Servlet重定向/路径需要加上项目名才能成功。
/helloworld.jsp:代表的是从当前项目下开始,SpringMVC会为路径自动的拼接上项目名。

    //重定向到helloworld.jsp页面
    @RequestMapping("/hello2")
    public String hello2(){
        return "redirect:/helloworld.jsp";
    }
    @RequestMapping("/hello3")
    public String hello3(){
        return "redirect:/hello2";
    }

SpringMvc视图解析流程

1、方法执行后的返回值会作为页面地址参考,转发或者重定向到页面 2、视图解析器可能会进行页面地址的拼串; 具体流程 1、任何方法的返回值,最终都会被包装成ModelAndView对象 image.png 2、processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);来到页面的方法 视图渲染流程:将域中的数据在页面展示;页面就是用来渲染模型数据的;

3、调用render(mv, request, response);渲染页面

4、View(interface)与ViewResolver(interface) ViewResolver的作用是根据视图名(方法的返回值)得到View对象; image.png

那么ViewReslover是如何根据方法的返回值(视图名),得到View对象的

protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
            HttpServletRequest request) throws Exception {

          //遍历所有的ViewResolver;
        for (ViewResolver viewResolver : this.viewResolvers) {
          //viewResolver视图解析器根据方法的返回值,得到一个View对象;
            View view = viewResolver.resolveViewName(viewName, locale);
            if (view != null) {
                return view;
            }
        }
        return null;
    }

resolveViewName(viewName, locale)方法的实现

@Override
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        if (!isCache()) {
            return createView(viewName, locale);
        }
        else {
            Object cacheKey = getCacheKey(viewName, locale);
            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对象;
                        **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);
                            if (logger.isTraceEnabled()) {
                                logger.trace("Cached view [" + cacheKey + "]");
                            }
                        }
                    }
                }
            }
            return (view != UNRESOLVED_VIEW ? view : null);
        }
    }

创建视图对象的方法实现createView(viewName, locale)

image.png

@Override
    protected View createView(String viewName, Locale locale) throws Exception {
        // If this resolver is not supposed to handle the given view,
        // return null to pass on to the next resolver in the chain.
        if (!canHandle(viewName, locale)) {
            return null;
        }
        // Check for special "redirect:" prefix.
        **if (viewName.startsWith(REDIRECT_URL_PREFIX))** {
            String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
            **RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());**
            return applyLifecycleMethods(viewName, view);
        }
        // Check for special "forward:" prefix.
        **if (viewName.startsWith(FORWARD_URL_PREFIX))** {
            String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
            return **new InternalResourceView(forwardUrl);**
        }
        // Else fall back to superclass implementation: calling loadView.
        //如果没有前缀就使用父类默认创建一个View;
        **return super.createView(viewName, locale);**
    }

image.png image.png

返回View对象:

1、视图解析器得到View对象的流程:所有已经配置了的解析器通过增强for循环尝试根据视图名(方法的返回值)得到View对象(视图对象);若能够得到就返回,得不到就继续进行for循环,切换到下一个视图解析器; 2、调用View对象的render方法

@Override
    public void render(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);
        }

        Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);

        prepareResponse(request, response);
        //渲染要给页面输出的所有数据
        renderMergedOutputModel(mergedModel, request, response);
    }

InternalResourceView中有renderMergedOutputModel方法

@Override
    protected void renderMergedOutputModel(
            Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

        // Determine which request handle to expose to the RequestDispatcher.
        HttpServletRequest requestToExpose = getRequestToExpose(request);

        // Expose the model object as request attributes.
        //将模型中的数据放在请求域中
        exposeModelAsRequestAttributes(model, requestToExpose);

        // Expose helpers as request attributes, if any.
        exposeHelpers(requestToExpose);

        // Determine the path for the request dispatcher.
        String dispatcherPath = prepareForRendering(requestToExpose, response);

        // Obtain a RequestDispatcher for the target resource (typically a JSP).
        RequestDispatcher rd = getRequestDispatcher(requestToExpose, 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 already included or response already committed, perform include, else forward.
        if (useInclude(requestToExpose, response)) {
            response.setContentType(getContentType());
            if (logger.isDebugEnabled()) {
                logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
            }
            rd.include(requestToExpose, 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(requestToExpose, response);
        }
    }

将模型中的所有数据取出来全放在request域中

  • 这也就是为什么隐含域中的数据能够在request域中
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
        for (Map.Entry<String, Object> entry : model.entrySet()) {
            String modelName = entry.getKey();
            Object modelValue = entry.getValue();
            if (modelValue != null) {
                request.setAttribute(modelName, modelValue);
                if (logger.isDebugEnabled()) {
                    logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +
                            "] to request in view with name '" + getBeanName() + "'");
                }
            }
            else {
                request.removeAttribute(modelName);
                if (logger.isDebugEnabled()) {
                    logger.debug("Removed model object '" + modelName +
                            "' from request in view with name '" + getBeanName() + "'");
                }
            }
        }
    }

总结

视图解析器只是为了得到View对象 对于转发操作(将数据模型全部放在请求域中)或者重定向到页面的操作是视图(View)对象做的事情 因此,视图对象才是真正渲染视图 image.png image.png


不论控制器返回一个String,ModelAndView,View都会转换为ModelAndView对象,由视图解析器解析视图,然后,进行页面的跳转 image.png

流程图

image.png

视图和视图解析器

  • 请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 String,View 或 ModeMap 等类型的处理方法,Spring MVC 也会在内部将它们- 装配成一个 ModelAndView 对象,它包含了逻辑名和模型对象的视图 Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是 JSP ,也可能是 Excel、JFreeChart等各种表现形式的视图
  • 对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现 MVC 的充分解耦

视图

  • 图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户。
  • 为了实现视图模型和具体实现技术的解耦,Spring 在 org.springframework.web.servlet 包中定义了一个高度抽象的 View 接口
  • 视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题

常用的视图实现类

image.png

视图解析器

SpringMVC 为逻辑视图名的解析提供了不同的策略,可以在 Spring WEB 上下文中配置一种或多种解析策略,并指定他们之间的先后顺序。每一种映射策略对应一个具体的视图解析器实现类。

  • 视图解析器的作用比较单一:将逻辑视图解析为一个具体的视图对象。
  • 所有的视图解析器都必须实现 ViewResolver 接口:

常用的视图解析器实现类

image.png

  • 程序员可以选择一种视图解析器或混用多种视图解析器
  • 每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性,可以通过 order 属性指定解析器的优先顺序,order 越小优先级越高。
  • SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则将抛出 ServletException 异常
  • InternalResourceViewResolver JSP 是最常见的视图技术,可以使用 InternalResourceViewResolve作为视图解析器

标签:解析器,return,SpringMvc,request,视图,View,解析,view
From: https://blog.51cto.com/u_15993308/6210809

相关文章

  • 火山引擎 DataLeap 下 Notebook 系列文章二:技术路线解析
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群在Jupyter的生态下,除了Notebook本身,火山引擎DataLeap研发团队还注意到了很多其他组件。彼时,JupyterLab正在逐渐取代传统的JupyterNotebook界面,成为新的标准。JupyterHub使用广泛,是......
  • HTML实现文件上传下载功能实例解析
    ​ 前言文件上传是一个老生常谈的话题了,在文件相对比较小的情况下,可以直接把文件转化为字节流上传到服务器,但在文件比较大的情况下,用普通的方式进行上传,这可不是一个好的办法,毕竟很少有人会忍受,当文件上传到一半中断后,继续上传却只能重头开始上传,这种让人不爽的体验。那有没有......
  • 论文解析 -- A Survey of AIOps Methods for Failure Management
    此篇Survey是ASystematicMappingStudyinAIOps的后续研究对于AIOPS中占比较高的FailureManagement进行进一步的研究   Comparedtotraditionalapproaches,AIOpsis:•fast,becauseitreactsindependentlyandautomaticallytoreal-timeproblems,with......
  • Spring源码系列:核心概念解析
    前言本文旨在为读者解析Spring源码中的关键类,以便读者在深入阅读源码时,能够了解关键类的作用和用途。在阅读Spring源码时,经常会遇到一些不熟悉的概念,了解关键类的作用可以帮助读者更好地理解这些概念。BeanDefinitionBeanDefinition是Spring框架中的一个重要概念,它定义了一个Be......
  • 2022上半年系统集成项目案例分析真题解析(广东卷)
    2022上半年系统集成项目案例分析真题解析(广东卷)......
  • 2022上半年系统集成项目综合知识真题及解析(广东卷)
    ......
  • day 09 9.1 数据解析之正则
    第三章.数据解析之正则RegularExpression,译作正则表达式或正规表示法,表示有规则的表达式,意思是说,描述一段文本排列规则的表达式。正则表达式并不是Python的一部分。而是一套独立于编程语言,用于处理复杂文本信息的强大的高级文本操作工具。正则表达式拥有自己独特的规则语法以......
  • C# Web实现文件上传下载功能实例解析
    ​IE的自带下载功能中没有断点续传功能,要实现断点续传功能,需要用到HTTP协议中鲜为人知的几个响应头和请求头。 一. 两个必要响应头Accept-Ranges、ETag        客户端每次提交下载请求时,服务端都要添加这两个响应头,以保证客户端和服务端将此下载识别为可以断点续传......
  • struts2文件上传的采用的三种方式解析
    文件上传几乎是每个项目实现的一个必须的模块。上传就是将信息从个人计算机(本地计算机)传递到中央计算机(远程计算机)系统上,让网络上的人都能看到。将制作好的网页、文字、图片等发布到互联网上去,以便让其他人浏览、欣赏。这一过程称为上传。JAVA实现文件上传的几个组件:......
  • 大揭秘!瑞芯微RK3568对比RK3399性能解析
    RK3568核心板简介​RK3568核心板是武汉万象奥科基于瑞芯微Rockchip的RK3568设计的一款高性能核心板。它采用四核Cortex-A55架构,最高主频可达2.0GHz,同时集成Mali-G522EEGPU,支持[email protected]/H.264/VP9解码和[email protected]/H.264编码。此外,RK3568支持多种接口,包括USB3.0、PC......