SpringMVC学习笔记(二)——SpringMVC工作流程

1.SpringMVC工作流程

 

 

 SpringMVC 的执行流程如下。

  1. 用户通过浏览器发起一个 HTTP 请求,该请求会被 DispatcherServlet(前端控制器)拦截;
  2. DispatcherServlet 调用 HandlerMapping(处理器映射器)找到具体的处理器(Handler)及拦截器,最后以 HandlerExecutionChain 执行链的形式返回给 DispatcherServlet。
  3. DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter(处理器适配器);
  4. HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler(即 Controller 控制器)对请求进行处理;
  5. Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象(Spring MVC 的底层对象,包括 Model 数据模型和 View 视图信息);
  6. HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet ;
  7. DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析;
  8. ViewResolver 解析完成后,会将 View 视图并返回给 DispatcherServlet;
  9. DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图);
  10. 视图负责将结果显示到浏览器(客户端)。

2.Spring MVC常用组件

Spring MVC 的常用组件共有 6 个,它们分别是: DispatcherServlet(前端控制器)、HandlerMapping(处理器映射器)、HandlerAdapter(处理器适配器)、Handler(处理器)、ViewResolver(视图解析器)和 View(视图)。

以上组件中,需要开发人员进行开发的是处理器(Handler,即 Controller)和视图(View)。通俗的说,要开发处理该请求的具体代码逻辑,以及最终展示给用户的界面。

 

 

 三、@Controller和@RequestMapping注解

从 Java 5 开始,Java 就增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、加载和运行时被读取,执行相应的处理。通过注解,开发人员可以在不改变原有代码逻辑的情况下,在代码中嵌入补充信息。
Spring 从 2.5 版本开始提供了对注解技术的全面支持,以替换传统的 XML 配置,简化 Spring 的配置。作为 Spring 框架的一个子项目, Spring MVC 自然也提供了对注解的支持。
在 Spring MVC 中有两个十分重要的注解,它们分别是 @Controller 和 @RequestMapping 。本节,我们就针对这两个重要 Spring MVC 注解进行讲解。

3.1 @Controller 注解

 @Controller 注解可以将一个普通的 Java 类标识成控制器(Controller)类,示例代码如下。
package net.biancheng.controller;
import org.springframework.stereotype.Controller;
@Controller
public class IndexController {
    // 处理请求的方法
}

Spring MVC 是通过组件扫描机制查找应用中的控制器类的,为了保证控制器能够被 Spring MVC 扫描到,我们还需要在 Spring MVC 的配置文件中使用 <context:component-scan/> 标签,指定控制器类的基本包(请确保所有控制器类都在基本包及其子包下),示例代码如下。

<!-- 使用扫描机制扫描控制器类,控制器类都在net.biancheng.controller包及其子包下 -->
<context:component-scan base-package="net.biancheng.controller" />

3.2 @RequestMapping 注解

 @RequestMapping 注解是 Spring MVC 中最常被用到的注解之一。它通常被标注在控制器方法上,负责将请求与处理请求的控制器方法关联起来,建立映射关系。
Spring MVC 的前端控制器(DispatcherServlet)拦截到用户发来的请求后,会通过 @RequestMapping 注解提供的映射信息找到对应的控制器方法,对这个请求进行处理。

3.2.1 @RequestMapping 注解的使用方式

 @RequestMapping 既可以标注在控制器类上,也可以标注在控制器方法上。

1. 修饰方法

当 @RequestMapping  注解被标注在方法上时,value 属性值就表示访问该方法的 URL 地址。当用户发送过来的请求想要访问该 Controller 下的控制器方法时,请求路径就必须与这个 value 值相同,示例代码如下。

@Controller
public class HelloController {
    @RequestMapping("/login")
    public String welcome() {
        return "login";
    }
}

2. 修饰类

当 @RequestMapping 注解标注在控制器类上时,value 属性的取值就是这个控制器类中的所有控制器方法 URL 地址的父路径。也就是说,访问这个 Controller 下的任意控制器方法都需要带上这个父路径。

例如,在上面的控制类中,用户想要访问 HelloController 中的 welcome() 方法,请求的地址就必须带上父路径“/springmvc”,即请求地址必须为“/springmvc/login”。

@Controller
@RequestMapping(value = "/springmvc")
public class HelloController {
    @RequestMapping("/login")
    public String welcome() {
        return "login";
    }
}

3.2.2 @RequestMapping 注解的属性

@RequestMapping 注解中提供了多个可用属性,下面我们就对其中几个比较常用的属性进行介绍。

1. value 属性

在 @RequestMapping 注解中,value 属性用来设置控制器方法的请求映射地址。所有能够匹配到该请求映射地址的请求,都可以被该控制器方法处理,示例代码如下。
@RequestMapping(value = "/register")

value 属性是 @RequestMapping 注解的默认属性,如果我们在 @RequestMapping 注解中只设置了一个 value 属性,则该属性名可以被省略,示例代码如下。

//省略 value 属性名
@RequestMapping( "/register")

value 属性的取值是一个字符串类型的数组,表示该控制器方法可以匹配多个请求地址。 

@RequestMapping( value = {"/register", "/login"})
public String success() {
    return "success";
}

2. name 属性

name 属性相当于方法的注释,用于解释这个方法是用来干什么的,使方法更易理解。
例如,下面的代码表示 getUsers() 方法是一个用来获取用户信息的控制器方法。

@RequestMapping(value = "toUser",name = "获取用户信息")
public String getUsers() {
    ……
}

3. method 属性

method 属性用来设置控制器方法支持的请求方式。如果一个控制器方法没有设置 @RequestMapping 注解的 method 属性,则说明该控制器方法支持全部请求类型,可以处理所有类型的请求。
method 属性的取值是一个 RequestMethod 类型的数组,表示一个控制器方法支持多种方式的请求,常用的请求方式有 GET、POST、DELETE、PUT 等。
例如,控制器方法只支持 GET 方式的请求,代码如下。

@RequestMapping(value = "/toUser",method = RequestMethod.GET)

我们也可以为同一个控制器方法指定支持多种类型的请求。例如,一个方法既支持 GET 方式的请求,也支持 POST 方式的请求,代码如下。

@RequestMapping(value = "/toUser",method = {RequestMethod.GET,RequestMethod.POST}),

4. params 属性

params 属性用于指定请求中的参数,只有当请求中携带了符合条件的参数时,控制器方法才会对该请求进行处理。
我们可以通过以下 4 种表达式来对请求的参数进行配置。

 

 

 params 属性的取值是一个字符串类型的数组,表示只有请求中同时携带了 params 属性指定的全部参数时,控制器方法才会对该请求进行处理。
例如,控制器方法 testParam() 的代码如下:

@RequestMapping(value = "/testParam", params = {"name=C语言中文网", "url=http://c.bianheng.net"})
@ResponseBody
public String testParam() {
    return "success";
}

以上代码表示,只有当请求中同时携带 name 和 url 两个请求参数,且参数值必须分别为 “C语言中文网” 和“http://c.biancheng.net”时,控制器方法 testParam() 才会对该请求进行处理 。

5. headers 属性

headers 属性用于设置请求中请求头信息,只有当请求中携带指定的请求头信息时,控制器方法才会处理该请求。
我们可以通过以下 4 种表达式来指定请求中的请求头信息。

 

 

header 属性是一个字符换类型的数组,表示只有当请求同时携带数组中规定的所有头信息时,控制器方法才会对该请求进行处理。
例如,控制器方法 method() 的代码如下。 

@RequestMapping(value = "toUser",headers = "Referer=http://c.biancheng.net")
public String metnod() {
    ……
}

在以上代码中,只有当请求的头信息中包含“Referer=http://c.biancheng.net”时,控制器方法 method() 才会处理该请求。

3.3 示例

见http://c.biancheng.net/spring_mvc/9670.html

4.SpringMVC获取请求参数的四种方式

Spring MVC 提供了多种获取请求参数的方式:

  • 通过 HttpServletRequest 获取请求参数
  • 通过控制器方法的形参获取请求参数
  • 使用 @RequestParam 注解获取请求参数
  • 通过实体类对象获取请求参数(推荐)

下面我们就对这些获取请求参数的方式一一进行介绍。

4.1 通过 HttpServletRequest 获取请求参数

我们可以在控制器方法中设置一个 HttpServletRequest 类型的形参,Spring MVC 会自动将请求中携带的参数封装到 HttpServletRequest 形参中,然后我们就可以通过 HttpServletRequest 提供的 getParameter() 方法获取所需的请求参数了。

示例

下面我们就通过一个简单的实例,来演示下如何通过 HttpServletRequest 获取请求参数。
1. 创建一个名为 springmvc-requestparam-demo 的 Web 项目,并将 Spring MVC 相关的依赖引入到项目中,web.xml 配置如下。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         id="WebApp_ID" version="4.0">
    <display-name>first-springmvc-demo</display-name>
    <!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--配置 DispatcherServlet 的一个初始化参数:spring mvc 配置文件按的位置和名称-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMVC.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--设置springMVC的核心控制器所能处理的请求的请求路径/所匹配的请求可以是/login或.html或.js或.css方式的请求路径但是/不能匹配.jsp请求路径的请求-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

2. 在 src 下(类路径下)创建一个名为 springMVC.xml 的 Spring MVC 配置文件,配置内容如下。

<?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="net.biancheng.c"></context:component-scan>
    <!-- 配置 Thymeleaf 视图解析器 -->
    <bean id="viewResolver"
          class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="order" value="1"/>
        <property name="characterEncoding" value="UTF-8"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
                        <!-- 视图前缀 -->
                        <property name="prefix" value="/WEB-INF/templates/"/>
                        <!-- 视图后缀 -->
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML5"/>
                        <property name="characterEncoding" value="UTF-8"/>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
</beans>

3.在 net.biancheng.c.controller 包下,新建一个名为 ParamController 的 Controller 类,代码如下。

package net.biancheng.c.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class ParamController {
    @RequestMapping("/")
    public String sayHello() {
        return "index";
    }
    /**
     * 通过 HttpServletRequest 获取请求参数
     * @param request
     * @return
     */
    @RequestMapping("/getRequestParam")
    public String requestParam(HttpServletRequest request) {
        String name = request.getParameter("name");
        String url = request.getParameter("url");
        System.out.println("name:" + name);
        System.out.println("url:" + url);
        return "index";
    }
}

4.将 springmvc-requestparam-demo 部署到 Tomcat 服务器中,启动 Tomcat,使用浏览器访问“http://localhost:8080/springmvc-requestparam-demo/getRequestParam?name=C语言中文网&url=c.biancheng.net”,控制台输出如下。

name:C语言中文网
url:c.biancheng.net

4.2 通过形参获取请求参数

我们可以在 Controller 的控制器方法中设置与请求参数同名的形参,以获取请求中携带的参数。当浏览器发送的请求匹配到这个控制器方法时,Spring MVC 会自动将请求参数赋值给相应的方法形参。
例如,当发送的请求的 url 为“http://localhost:8080/project/test?name=tom&language=java”时,那么处理该请求的控制器方法的代码如下。

@RequestMapping("/test")
public String test(String name, String language) {
    System.out.println("a:" + a);
    System.out.println("b:" + b);
    return "success";
}

通过控制器方法的形参获取请求参数时,我们需要注意以下几点。

1. 必须保证参数名一致

我们必须保证控制器方法的形参名称与请求中携带参数的名称完全一致(区分大小写),否则控制器方法接收到的请求参数值会是 null。

如果由于一些特殊原因,实在无法保证参数名严格一致,我们还可以通过 @RequestParam 注解来解决。

2. 无视数据类型

这种方式是无视参数的数据类型的,我们可以在控制器方法中使用 String 字符串类型的形参接收所有的请求参数,也可以根据实际情况在控制器方法中使用对应数据类型的参数来接收请求参数,而无须自行进行数据类型转换。

例如,请求 url 为“http://localhost:8080/project/test?a=java&b=1025”,此时我们可以通过以下 2 种方式来获取请求参数。

1) 直接通过 String 类型的参数来接收请求参数。

@RequestMapping("/test")
public String test(String a, String b) {
    System.out.println("a:" + a);
    System.out.println("b:" + b);
    return "success";
}

2)使用 int 或 Integer 类型的参数接收 b 参数。

@RequestMapping("/test")
public String test(String a, Integer b) {
    System.out.println("a:" + a);
    System.out.println("b:" + b);
    return "success";
}

3. 不适用于请求参数过多的请求

当请求中携带的参数过多时,如果我们还使用这种方式来获取请求参数,那就需要我们在控制器方法中设置大量的形参,这会让使代码变得十分臃肿,不易维护。

4. 同名请求参数的处理方式

当请求中包含多个同名的请求参数时,我们可以通过以下 2 种类型的形参来获取请求参数。

 4.3 使用 @RequestParam 注解获取

 我们可以在控制器方法中通过 @RequestParam 注解,在请求参数与控制器方法的形参之间建立起映射关系,将它们绑定起来。这样即使请求参数与控制器方法中的形参名称不一致,我们也能获取到对应的请求参数值。
例如,如果请求的地址为“http://localhost:8080/project/test?name=Java&pass=yyds”,那么负责处理该请求的控制器方法可以是这样的,代码如下。
@RequestMapping("/testRequestParam")
public String testRequestParam(@RequestParam("name") String username, @RequestParam("pass") String password) {
    System.out.println(username + "," + password);
    return "success";
}

@RequestParam 注解中共包含 4 个属性,如下表所示。

 

 4.4 通过实体类对象获取(推荐)

 我们可以在 Controller 控制器方法的形参中设置一个实体类形参,如果请求参数的参数名与实体类中的属性名一致,那么 Spring MVC 会自动将请求参数封装到该实体类对象中。此时我们就可以通过该实体类对象获取所需的请求参数了。

注:我们推荐大家使用实体类对象来获取请求参数,这也是最常用的请求参数的方式之一,它能够有效地解决“控制器方法形参不适用于请求参数过多的请求”问题。

下面,我们通过一个简单的实例,来演示下如何通过实体类获取请求参数。
1. 在 springmvc-requestparam-demo 的 net.biancheng.c.entity 包下,创建一个名为 User 的实体类,代码如下。

public class User {
    private String UserId;
    private String UserName;
    private Integer age;
    public String getUserId() {
        return UserId;
    }
    public void setUserId(String userId) {
        UserId = userId;
    }
    public String getUserName() {
        return UserName;
    }
    public void setUserName(String userName) {
        UserName = userName;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "UserId='" + UserId + '\'' +
                ", UserName='" + UserName + '\'' +
                ", age=" + age +
                '}';
    }
}

2. 在 net.biancheng.c.controller 包下,创建一个名为 UserController 的 Controller 类,代码如下。

import net.biancheng.c.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class UserController {
    /**
     * 通过实体类获取请求参数
     *
     * @param user
     * @return
     */
    @RequestMapping("/getUser")
    public String getUser(User user) {
        System.out.println("userId:" + user.getUserId());
        System.out.println("userName:" + user.getUserName());
        System.out.println("password:" + user.getPassword());
        return "success";
    }
}

3. 修改 webapp/WEB-INF/tempaltes 目录中的 user.html,代码如下。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>C语言中文网</title>
</head>
<body>
<form th:action="@{/getUser}" method="post">
    <table style="margin: auto">
        <tr>
            <td>用户 ID:</td>
            <td><input type="text" name="userId" required><br></td>
        </tr>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="userName" required><br></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="password" required><br></td>
        </tr>
        <tr>
            <td colspan="2" align="center"><input type="submit"></td>
        </tr>
    </table>
</form>
</body>
</html>

5. 重启 Tomcat 服务器,使用浏览器访问“http://localhost:8080/springmvc-requestparam-demo/user”,结果如下图。

 

 6. 在表单中分别输入用户 ID:123456789,用户名:java-user,密码:qwertyuiop,如下图。

 

 7. 点击下面的“提交”按钮,控制台输入的内容如下。

userId:123456789
userName:java-user
password:qwertyuiop

 4.5 解决获取请求参数的乱码问题

 当我们在 post 请求中传递的参数为中文时,控制器方法获取到的参数值会出现乱码的情况。
例如,我们使用浏览器访问“http://localhost:8080/springmvc-requestparam-demo/user”,并在表单中的用户名输入“C语言中文网”,如下图。

 

 

 点击提交按钮后,控制台输出如下。

userId:123456789
userName:Cè??è¨??????????
password:987654321
 解决办法:

Spring MVC 默认提供了一个过滤器 CharacterEncodingFilter,我们只需要在 web.xml 中对该 Filter 进行简单的配置,即可解决请求和响应中的中文乱码问题,代码如下。

<!--请求和响应的字符串过滤器-->
<filter>
     <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--设置请求的编码-->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <!--设置响应的编码,这里我们可以省略-->
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

重启 Tomcat 服务器,再次访问“http://localhost:8080/springmvc-requestparam-demo/user”,再次在表单中输入如图 5 的内容,最后点击提交按钮,控制台输入如下。

userId:123456789
userName:C语言中文网
password:987654321

通常情况下,我们都需要在 Spring MVC 的 web.xml 中对 CharacterEncodingFilter 进行配置,以防止请求或响应信息中出现中文乱码。

 5.Spring MVC域对象共享数据

 在 Spring MVC 中,控制器在接收到 DispatcherServlet 分发过来的请求后,会继续调用 Model 层对请求进行处理。Model 层处理完请求后的结果被称为模型数据,会将模型数据返回给 Controller。Controller 在接收到 Model 层返回的模型数据后,下一步就是将模型数据通过域对象共享的方式传递给 View 视图进行渲染,最终返回给客户端展示。

域对象是服务器在内存上创建的一块存储空间,主要用不同动态资源之间的数据传递和数据共享。在 Spring MVC 中,常用的域对象有 request 域对象、session 域对象、application 域对象等。

 Spring MVC 提供了多种域对象共享数据的方式,其中最常用的方式如下:

  • 使用 Servlet API 向 request 域对象中共享数据
  • 使用 ModelAndView 向 request 域对象中共享数据
  • 使用 Model 向 request 域对象中共享数据
  • 使用 Map 向 request 域对象中共享数据
  • 使用 ModelMap 向 request 域对象中共享数据
  • 使用 Servlet API 向 session 域中共享数据
  • 使用 Servlet API 向 application 域中共享数据

 5.1  使用 Servlet API 向 request 域对象中共享数据

 我们可以在控制器方法中设置一个 HttpServletRequest 类型的形参。通过它,我们就可以将模型数据共享到 request 域对象中,示例代码如下。

@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request) {
    request.setAttribute("testScope", "hello,Servet API");
    return "success";
}

由于这种方式是通过原生 Servlet API 实现的,会导致控制器与 Servlet 容器耦合度过高,因此通常情况下,我们不推荐使用这种方式向 request 域对象中共享数据。

5.2 使用 ModelAndView 向 request 域对象中共享数据

 我们还可以通过 Spring 提供的 ModelAndView 向 reuqest 域对象中共享数据。ModelAndView 主要由 model(模型)和 view(视图)两部分组成。其中,model 负责数据共享,而 view 则主要用于设置视图,实现页面的跳转。
ModelAndView 为我们提供了多种方法,其中常用的方法如下表。

 在 Controller 类中,ModelAndView 只有在作为控制器方法的返回值,返回给前端控制器(DispatcherServlet)时,前端控制器解析才会去解析它,示例代码如下。

@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
     /**
     * ModelAndView有Model和View的功能
     * Model主要用于向请求域共享数据
     * View主要用于设置视图,实现页面跳转
     */
    ModelAndView mav = new ModelAndView();
    //向请求域共享数据
    mav.addObject("testScope", "hello,ModelAndView");
    //设置视图,实现页面跳转
    mav.setViewName("success");
    return mav;
}

 5.3 使用 Model 向 request 域对象中共享数据

 我们可以在 Controller 控制器方法中设置一个 Model 类型的形参。通过它,我们也可以向 request 域对象中共享数据,示例代码如下。

@RequestMapping("/testModel")
public String testModel(Model model) {
    model.addAttribute("testScope", "hello,Model");
    return "success";
}

5.4 使用 Map 向 request 域对象中共享数据

 我们可以在 Controller 控制器方法中设置一个 Map 类型的形参。通过它,我们也可以向 request 域对象中共享数据,示例代码如下。

@RequestMapping("/testMap")
public String testMap(Map<String, Object> map) {
    map.put("testScope", "hello,Map");
    return "success";
}

5.5 使用 ModelMap 向 request 对象中共享数据

 我们可以在 Controller 控制器方法中设置一个 ModelMap 类型的形参。通过它,我们也可以向 request 域对象中共享数据,示例代码如下。

@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap) {
    modelMap.addAttribute("testScope", "hello,ModelMap");
    return "success";
}

 5.6 使用 Servlet API 向 session 域对象中共享数据

 我们可以在控制器方法中设置一个 HttpSession 类型的形参。通过它,我们就可以将数据共享到 session 域对象中,示例代码如下。

@RequestMapping("/testSession")
public String testSession(HttpSession session) {
    session.setAttribute("testSessionScope", "hello,session");
    return "success";
}

5.7 使用 Servlet API 向 application 域对象中共享数据

我们可以在控制器方法中设置一个 HttpSession 类型的形参,并通过它获取到 application 域对象,最终我们就可以将数据共享到 application 域对象中,示例代码如下。

@RequestMapping("/testApplication")
public String testApplication(HttpSession session) {
    ServletContext application = session.getServletContext();
    application.setAttribute("testApplicationScope", "hello,application");
    return "success";
}

5.8 这里有一个完整的示例:从前端界面到Controller层、到Service层再到Dao层

http://c.biancheng.net/spring_mvc/9672.html

6.Spring MVC 视图和视图解析器

 Spring MVC 的控制器方法支持 ModelAndView、ModelMap、View、String 多种类型的返回值,但无论控制器方法的返回值是哪种类型,Spring MVC 内部最终都会将它们封装成一个 ModelAndView 对象。
ModelAndView 对象由 model(模型数据)和 view(视图)两部分组成,但这里的 view 通常并不是一个真正的 View 视图对象,而仅仅是一个 String 类型的逻辑视图名(View Name)而已,例如“success”、“index”等。这种情况下,Spring MVC 就需要借助 ViewResolver(视图解析器)将 ModelAndView 对象中逻辑视图名解析为真正的 View 视图对象,然后才能响应给客户端展示。
Spring MVC 的核心理念是将 View 视图与 Model 模型进行解耦,其工作重心聚焦在 Model(模型)数据上。至于最终究竟采用何种视图技术对模型数据进行渲染,它并不关心,更不会强迫用户使用某种特定的视图实现技术。因此我们可以在 Spring MVC 项目中,根据自身需求自由地选择所需的视图技术,例如 Thymeleaf、JSP、FreeMarker、Velocity、Excel 等等。

6.1 视图

 我们知道,Spring MVC 是一款基于 MVC 模式的 Web 开发框架,这里所说的 V 指的就是 View,即“视图”。
在 Spring MVC 中,视图扮演着十分重要的角色,它主要负责整合 Web 资源、对模型数据进行渲染,并最终将 Model 中的数据以特定的形式展示给用户。
通俗点说,View 就是用来渲染页面的,它目的是将程序返回的数据(Model 数据)填入到页面中,最终生成 HTML、JSP、Excel 表单、Word 文档、PDF 文档以及 JSON 数据等形式的文件,展示给用户。

View 接口

Spring MVC 在 org.springframework.web.servlet 包中定义了一个高度抽象的 View(视图)接口,该接口中共定义了两个方法,如下表。

 

常用视图类

为了简化视图的开发,Spring MVC 为我们提供了许多已经开发好的视图,这些视图都是 View 接口的实现类。
下表中列举了几个常用的视图,它们中的每一个都对应 Java Web 中的特定视图技术。

 

 

 6.2 视图的分类

 我们可以将 Spring MVC 中 View 视图划分为两大类:逻辑视图和非逻辑视图。

逻辑视图

逻辑视图最大的特点就是,其控制器方法返回的 ModelAndView 中的 view 可以不是一个真正的视图对象,而是一个字符串类型的逻辑视图名。对于逻辑视图而言,它需要一个视图解析器(ViewResolver)进行解析,才能得到真正的物理视图对象。
在 Spring MVC 中,控制器方法返回逻辑视图名的方式一般以下有两种。
1. 直接在控制器方法中返回字符串类型的逻辑视图名,然后通过与 Model、Map、ModelMap 等对象的配合将 Model(模型)数据带入到视图中,示例代码如下。

@RequestMapping("/testView")
public String testView(Model model) {
    model.addAttribute("product","模型数据")
    return "success";
}

2. 在控制器方法中通过 ModelAndView 提供的 setViewName() 方法设置逻辑视图名,然后通过 ModelAndView 的 addObject() 等方法将模型数据带入到视图中,示例代码如下。

@RequestMapping("/testView")
public ModelAndView testView() {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("productList");
    List<Product> productList = productService.getProductList();
    modelAndView.addObject(productList);
    return modelAndView;
}

非逻辑视图

非逻辑视图,则与逻辑视图完全相反,其控制方法返回的是一个真正的视图对象,而不是逻辑视图名,因此这种视图是不需要视图解析器解析的,只需要直接将视图模型渲染出来即可,例如 MappingJackson2JsonView 就是这样的情况。
MappingJackson2JsonView 的目的就是将数据模型转换为 JSON 视图,展现给用户,无须对视图名字再进行下一步的解析。

@RequestMapping("/testJsonView")
public ModelAndView testJsonView(Integer productId) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("url", "c.biancheng.net");
    //设置 ModelAndView 的 View 对象
    modelAndView.setView(new MappingJackson2JsonView());
    return modelAndView;
}

在上面的代码中,我们通过 ModelAndView 的 setView() 方法构造了一个具体的 MappingJackson2JsonView 视图对象,该视图可以直接渲染,最终将 Model 数据转换为 JSON 数据。

6.3 视图解析器(只有逻辑视图才需要视图解析器来解析)

视图解析器(ViewResolver)是 Spring MVC 的重要组成部分,它提供了逻辑视图名称与实际视图之间的映射,负责将逻辑视图名解析为一个具体的视图对象。
SpringMVC 提供了一个视图解析器的接口 ViewResolver,所有具体的视图解析器必须实现该接口。

public interface ViewResolver {
    @Nullable
    View resolveViewName(String viewName, Locale locale) throws Exception;
}

Spring MVC 提供了很多 ViewResolver 接口的实现类,它们中的每一个都对应 Java Web 应用中某些特定视图技术。如果我们在使用某个特定的视图解析器,就需要将它以 Bean 组件的形式注入到 Spring MVC 的容器中,否则 Spring MVC 会使用默认的 InternalResourceViewResolver 进行解析。

 

 我们知道,非逻辑视图是不需要视图解析器进行解析的,例如 MappingJackson2JsonView ,它的含义是将当前的数据模型转换为 JSON,并不需要对视图逻辑名称进行转换。但对于逻辑视图而言,将逻辑视图名转换为视图却是一个不可缺少的过程。
以 ThymeleafView 为例,它就是一个十分典型的逻辑视图,想要使用 Thymeleaf 进行前端页面开发,通常都需要在 Spring MVC 的配置文件中配置一个 Thymeleaf 视图解析器,示例配置如下。

<!-- 配置 Thymeleaf 视图解析器 -->
<bean id="viewResolver"
      class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
    <!--定义视图解析器的优先级,order 值越小,优先级越高-->
    <property name="order" value="1"/>
    <!--定义视图文件的字符集-->
    <property name="characterEncoding" value="UTF-8"/>
    <property name="templateEngine">
        <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
            <property name="templateResolver">
                <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
                    <!-- 视图前缀 -->
                    <property name="prefix" value="/WEB-INF/templates/"/>
                    <!-- 视图后缀 -->
                    <property name="suffix" value=".html"/>
                    <property name="templateMode" value="HTML5"/>
                    <property name="characterEncoding" value="UTF-8"/>
                </bean>
            </property>
        </bean>
    </property>
</bean>

6.4 同时配置多个视图解析器

这部分代码见:http://c.biancheng.net/spring_mvc/9673.html

6.5 视图控制器

如果控制器方法只返回一个逻辑视图名,而没有返回任何 Model 数据,那么这个控制器方法就可以使用 View-Controller(视图控制器)标签来代替。
例如,下面的控制器方法只返回一个逻辑视图名“add”,而没有返回任何 Model 数据,即它仅仅是用来跳转到“新增”页面的,代码入下。

@RequestMapping("/addPage")
public String addPage() {
    return "base/add";
}

此时,我们就可以在 Spring MVC 中通过以下配置来代替这个控制器方法,配置内容如下。

<mvc:view-controller path="/addPage" view-name="base/add"></mvc:view-controller>

注意:如果 Spring MVC 中设置了任意一个视图控制器(View-Controller),那么其他控制器中请求映射将全部失效,此时我们需要在 Spring MVC 的核心配置文件中开启 mvc 注解驱动标签。

<mvc:annotation-driven />

 

 

 

 

 
 
posted @ 2022-09-03 18:04  一直学习的程序小白  阅读(86)  评论(0编辑  收藏  举报