Spring MVC 中视图的实现原理,在Spring MVC 中实现重定向和转发,以及访问静态资源

时间:2024-07-15 15:48:42

1. Spring MVC 中视图的实现原理,在Spring MVC 中实现重定向和转发,以及访问静态资源

文章目录

  • 1. Spring MVC 中视图的实现原理,在Spring MVC 中实现重定向和转发,以及访问静态资源
    • 1.1 Spring MVC视图支持可配置
    • 1.2 Spring MVC支持的常见视图
    • 1.3 实现视图机制的核心接口
    • 1.4 实现视图机制的原理描述
    • 1.5 逻辑视图名到物理视图名的转换
  • 2. Thymeleaf视图
  • 3. JSP视图(了解)
  • 4. 转发与重定向
    • 4.1 转发和重定向区别
  • 5. 在 Spring MVC 中 “重定向” 以及 “转发”
    • 5.1 在 Spring MVC 中使用 forward 实现转发
    • 5.2 在 Spring MVC 中使用 redirect 实现重定向
  • 6. mvc:view-controller 和 mvc:annotation-driven/ 的配合使用
  • 7. Spring MVC 中访问静态资源
    • 7.1 第一种方案:开启默认的 Servlet处理
    • 7.2 第二种方案:使用 mvc:resources 标签配置静态资源
  • 8. 总结:
  • 9. 最后:


1.1 Spring MVC视图支持可配置

在Spring MVC中,视图 View 是支持定制的,例如我们之前在 springmvc.xml 文件中进行了如下的配置:

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<!--组件扫描-->
    <context:component-scan base-package="com.rainbowsea.springmvc.controller"></context:component-scan>

<!--    视图解析器-->
    <!--    视图解析器-->
    <bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
        <!--作用于视图渲染的过程中,可以设置视图渲染后输出时采用的编码字符集-->
        <property name="characterEncoding" value="UTF-8"/>
        <!--如果配置多个视图解析器,它来决定优先使用哪个视图解析器,它的值越小优先级越高-->
        <property name="order" value="1"/>
        <!--当 ThymeleafViewResolver 渲染模板时,会使用该模板引擎来解析、编译和渲染模板-->
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring6.SpringTemplateEngine">
                <!--用于指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器负责根据模板位置、模板资源名称、文件编码等信息,加载模板并对其进行解析-->
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
                        <!--设置模板文件的位置(前缀)-->
                        <property name="prefix" value="/WEB-INF/templates/"/>
                        <!--设置模板文件后缀(后缀),Thymeleaf文件扩展名不一定是html,也可以是其他,例如txt,大部分都是html-->
                        <property name="suffix" value=".html"/>
                        <!--设置模板类型,例如:HTML,TEXT,JAVASCRIPT,CSS等-->
                        <property name="templateMode" value="HTML"/>
                        <!--用于模板文件在读取和解析过程中采用的编码字符集-->
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
</beans>

以上的配置表明当前 Spring MVC 框架使用的视图 View 是 Thymelea 的。

如果你需要换成其他的视图 View,修改以上的配置即可。这样就可以非常轻松的完成视图 View 的扩展。

这种设计是完成符合 OCP 开闭原则 的。视图 View 和框架是解耦合的,耦合度低扩展能力强。

视图 View 可以通过配置文件进行灵活切换。

1.2 Spring MVC支持的常见视图

Spring MVC支持的常见视图包括:

  1. InternalResourceView:内部资源视图(Spring MVC框架内置的,专门为JSP模板语法准备的)
  2. RedirectView:重定向视图(Spring MVC框架内置的,用来完成重定向效果)
  3. ThymeleafView:Thymeleaf视图(第三方的,为Thymeleaf模板语法准备的)
  4. FreeMarkerView:FreeMarker视图(第三方的,为FreeMarker模板语法准备的)
  5. VelocityView:Velocity视图(第三方的,为Velocity模板语法准备的)
  6. PDFView:PDF视图(第三方的,专门用来生成pdf文件视图)
  7. ExcelView:Excel视图(第三方的,专门用来生成excel文件视图)

1.3 实现视图机制的核心接口

实现视图的核心类与接口包括:

DispatcherServlet类(前端控制器):

  1. 职责:在整个Spring MVC执行流程中,负责*调度。
  2. 核心方法:doDispatch

在这里插入图片描述


ViewResolver 接口(视图解析器)

  1. 职责:负责将 逻辑视图名 转换为 物理视图名 ,最终创建 View 接口的实现类,即视图实现类对象。

  2. 核心方法:resolveViewName

在这里插入图片描述

View 接口(视图):

  1. 职责:负责将模型数据 Model 渲染为视图格式(HTML代码),并最终将生成的视图(HTML代码)输出到客户端。(它负责将模板语言转换成 HTML代码)
  2. 核心方法:render

在这里插入图片描述

ViewResolverRegistry (视图解析器注册器):

负责在 web 容器(Tomcat)启动的时候,完成视图解析器的注册。

如果有多个视图解析器,会将视图解析器对象按照 order 的配置放入 List 集合。

总结:

实现视图的核心类和接口包括:ViewResolverRegistry,DispatcherServlet,ViewResolver,View

如果你想定制自己的视图组件:

  • 编写类实现 ViewResolver 接口,实现 resolveViewName方法,在该方法中完成 逻辑视图名 转换为物理视图名 ,并返回 View 对象。
  • 编写类实现 View 接口,实现 render 方法,在该方法中将模板语言转换成 HTML代码,并将HTML代码响应到浏览器。

如果 Spring MVC 框架中使用 Thymeleaf 作为视图技术。那么相关的类包括:

  • ThymeleafView
  • ThymeleafViewResolver

1.4 实现视图机制的原理描述

假设我们Spring MVC 中使用了 Thymeleaf 作为视图:

  1. 第一步:浏览器发送请求给 web 服务器
  2. 第二步:Spring MVC中的 DispatcherServlet 接收到请求
  3. 第三步:DispatcherServlet 根据请求路径分发到对应的 Controller
  4. 第四步:DispatcherServlet 调用 Controller 的方法
  5. 第五步:Controller 的方法处理业务并返回一个 逻辑视图名 给 DispatcherServlet
  6. 第六步:DispatcherServlet 调用 ThymeleafViewResovler 的 resolveViewName方法,将逻辑视图名 转换为 物理视图名 ,并创建 ThymeleafView 对象返回给 DispatcherServlet
  7. 第七步:DispatcherServlet 再调用 ThymeleafView 的 render 方法,render 方法将模板语言转换为 HTML代码,响应给浏览器,完成最终的渲染。

假设我们 Spring MVC 中使用了JSP作为视图:

  1. 第一步:浏览器发送请求给 web 服务器
  2. 第二步:Spring MVC 中的DispatcherServlet 接收到请求
  3. 第三步:DispatcherServlet 根据请求路径分发到对应的 Controller
  4. 第四步:DispatcherServlet 调用 Controller 的方法
  5. 第五步:Controller 的方法处理业务并返回一个 逻辑视图名 给 DispatcherServlet
  6. 第六步:DispatcherServlet 调用 InternalResourceViewResovlerresolverViewName 方法,将逻辑视图 转换为 物理视图名 ,并创建 InternalResourceView 对象返回给 DispathcerServlet
  7. 第七步:DispatcherServlet 再调用 InternalResourceView 的 render 方法,render 方法将模板语言转换为 HTML 代码,响应给浏览器,完成最终的渲染。

1.5 逻辑视图名到物理视图名的转换

逻辑视图名最终转换的物理视图名是什么,取决再 springmvc.xml 文件中视图解析器的配置: 假如视图解析器配置的是 ThymeleafViewResolver,如下:

<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
    <property name="characterEncoding" value="UTF-8"/>
    <property name="order" value="1"/>
    <property name="templateEngine">
        <bean class="org.thymeleaf.spring6.SpringTemplateEngine">
            <property name="templateResolver">
                <bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
                    <property name="prefix" value="/WEB-INF/templates/"/>
                    <property name="suffix" value=".html"/>
                    <property name="templateMode" value="HTML"/>
                    <property name="characterEncoding" value="UTF-8"/>
                </bean>
            </property>
        </bean>
    </property>
</bean>

以下程序返回逻辑视图名:index

在这里插入图片描述

package com.rainbowsea.springmvc.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller  // 交给 Spring IOC 容器管理起来
public class IndexController {

    @RequestMapping("/")
    public String index() {
        return "index";


    }
}

最终逻辑视图名 “index” 转换为物理视图名: /WEB-INF/templates/index.html

在这里插入图片描述


假如视图解析器配置的是 InternalResourceViewResolver,如下:

在这里插入图片描述

<!--    jsp视图解析器-->
    <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

以下程序返回逻辑视图名:index

package com.rainbowsea.springmvc.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller  // 交给 Spring IOC 容器管理起来
public class IndexController {

    @RequestMapping("/")
    public String index() {
        return "index";


    }
}

最终逻辑视图名 “index” 转换为物理视图名:/ WEB-INF/templates/index.jsp

在这里插入图片描述

2. Thymeleaf视图

我们在学习前面内容的时候,采用的都是 Thymeleaf视图。我们再来测试一下,看看底层创建的视图对象是不是ThymeleafView
springmvc.xml 配置内容如下:

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--组件扫描-->
    <context:component-scan base-package="com.powernode.springmvc.controller"/>

    <!--视图解析器-->
    <bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
        <property name="characterEncoding" value="UTF-8"/>
        <property name="order" value="1"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring6.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
                        <property name="prefix" value="/WEB-INF/thymeleaf/"/>
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML"/>
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
</beans>

Controller代码如下:

在这里插入图片描述

package com.rainbowsea.springmvc.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller  // 交给 Spring IOC 容器管理起来
public class IndexController {

    @RequestMapping("/")
    public String index() {
        return "index";


    }
}

视图页面:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>index page</title>
</head>
<body>
<h1>index page</h1>
</body>
</html>

添加断点:在 DispatcherServlet 的 **doDispatch( )**方法的下图位置添加断点。

在这里插入图片描述

启动Tomcat,在浏览器地址栏上发送请求:http://localhost:8080/springmvc/index

在这里插入图片描述

程序走到以上位置,这行代码是调用对应的Controller,并且Controller最终会返回 ModelAndView对象:mv 按照我们之前所讲,返回 mv 之后,接下来就是视图处理与渲染,接着往下走,走到下图这一行:

在这里插入图片描述

这个方法的作用是处理分发结果,就是在这个方法当中进行了视图的处理与渲染,进入该方法:

在这里插入图片描述

进去之后走到上图位置:这个方法就是用来渲染页面的方法,再进入该方法:

在这里插入图片描述

走到上图位置就可以看到底层创建的是 ThymeleafView 对象。

3. JSP视图(了解)

我们再来跟一下源码,看看 JSP 视图底层创建的是不是 InternalResourceView对象。
我们前面说过 InternalResourceView是SpringMVC框架内置的,翻译为内部资源视图,SpringMVC把JSP看做是内部资源。可见JSP在之前的技术栈中有很高的地位。
不过,当下流行的开发中JSP使用较少,这里不再详细讲解。只是测试一下。

springmvc.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--组件扫描-->
    <context:component-scan base-package="com.powernode.springmvc.controller"/>

    <!--视图解析器-->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

Controller代码如下:

package com.powernode.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {
    @RequestMapping("/index")
    public String toIndex(){
        return "index";
    }
}

视图页面:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>index jsp</title>
  </head>
  <body>
    <h1>index jsp!</h1>
  </body>
</html>

启动web容器,添加断点跟踪:

在这里插入图片描述

通过测试得知:对于JSP 视图来说,底层创建的视图对象是 InternalResourceView。

4. 转发与重定向

4.1 转发和重定向区别

  1. 转发是一次请求,因此浏览器地址栏上的地址不会发生变化。
  2. 重定向是两次请求,因此浏览器地址栏上的地址会发生变化。
  3. 转发的代码实现:request.getRequestDispatcher(“/index”).forward(request, response);
  4. 重定向的代码实现:response.sendRedirect(“/webapproot/index”);
  5. 转发是服务器内部资源跳转,由服务器来控制。不可实现跨域访问。
  6. 重定向可以完成内部资源的跳转,也可以完成跨域跳转。
  7. 转发的方式可以访问 WEB-INF 目录下受保护的资源。
  8. 重定向相当于浏览器重新发送了一次请求,在浏览器直接发送的请求是无法访问 WEB-INF目录下受保护的资源的。
  9. 转发原理:
  1. 假设发送了 /a 请求,执行了 AServlet
  2. 在 AServlet 中通过 request.getRequestDispatcher("/b").forward(request,response); 转发到 BServlet
  3. 从AServlet 跳转到 BServlet 是服务器内部来控制的。对于浏览器而言,浏览器只发送了一个 /a 请求。
  1. 重定向原理:
  1. 假设发送了 /a 请求,执行了 AServlet
  2. 在 AServlet 中通过 response.sendRedirect("/webapproot/b")
  3. 此时服务器会将请求路径 /webapproot/b 响应给浏览器
  4. 浏览器会自发的再次发送 webapproot/b 请求来访问 BServlet
  5. 因此对于重定向来说,发送了两次请求,一次是 /webapproot/a ,另一次是 webapproot/b

更多关于 Java Web 转发和重定向的内容,大家可以移步至:✏️✏️✏️ JavaWeb 中 “转发”与 “重定向”的区别_java 重定向与转发-****博客

以上所描述的是使用原生Servlet API来完成转发和重定向。在Spring MVC中是如何转发和重定向的呢?

5. 在 Spring MVC 中 “重定向” 以及 “转发”

5.1 在 Spring MVC 中使用 forward 实现转发

在Spring MVC中默认就是转发的方式,我们之前所写的程序,都是转发的方式。

Spring MVC 中是怎么通过代码完成转发的?
@RequestMapping("/a")
public String toA() {
    // 返回的是一个逻辑视图名称
    return "a";  // 转发 ThymeleafView
}

注意,当 return "a"; 的时候,返回一个逻辑视图名称,这种方式跳转到视图,默认采用的就是 forward 方式跳转过去的

只不过都是转发到 Thymeleaf 的模板文件xxx.html上。
那么,在Spring MVC中如何转发到另一个Controller上呢?可以使用Spring MVC的forward
使用格式如下:

return "forward:/b";转发到 /b ,这是一次请求,底层创建的视图对象是:internalResourceView对象
注意: "forward:/b" 这个已经不是逻辑视图名称了,是以转发的方式跳转,是一个资源路径。

在这里插入图片描述

package com.rainbowsea.springmvc.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller // 交给 Spring IOC 容器进行管理
public class ForwardController {

    @RequestMapping("/a")
    public String toA() {
        // 返回的是一个逻辑视图名称
        //return "a";

        // 采用SpringMVC的转发方式跳转到 /b
        // 转发的时候,格式有特殊要求,return "forward:下一个资源的路径"
        // 这种方式就不是逻辑视图名称了
        return "forward:/b"; // 创建InternalResourceView对象

        // 这个使用较多,重定向,url 中会显示资源路径
        // return "redirect:/b";
    }


    @RequestMapping("/b")
    public String