Spring MVC数据绑定和响应

数据绑定

在程序运行时,Spring MVC接收到客户端的请求后,会根据客户端请求的参数和请求头等数据信息,将参数以特定的方式转换并绑定到处理器的形参中。Spring MVC中将请求消息数据与处理器的形参建立连接的过程就是Spring MVC的数据绑定。

Spring MVC数据绑定的过程图

Spring MVC数据绑定中的信息处理过程 

  1. Spring MVC将ServletRequest对象传递给DataBinder。
  2. 将处理方法的入参对象传递给DataBinder。
  3. DataBinder调用ConversionService组件进行数据类型转换、数据格式化等上作,并将ServletRequest对象中的消息填充到参数对象中。
  4. 调用Validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验。
  5. 校验完成后会生成数据绑定结果BindingResult对象,Spring MVC会将BindingResult对象中的内容赋给处理方法的相应参数。

简单数据绑定 

默认类型数据绑定

Spring MVC常见的默认类型

当使用Spring MVC默认支持的数据类型作为处理器的形参类型时,Spring MVC的参数处理适配器会默认识别这些类型开进行赋值。Spnng MVC常见的默认类型如下所示。

  • HttpServletRequest:获取请求信息。
  • HttpServletResponse:处理响应信息。
  • HttpSession:获取session中存放的对象。
  • Model/ModelMap : Model是一个接口,ModelMap是一个类,Model的实现类对象和ModelMap对象都可以设置model数据,model数据会填充到request域。

案例演示默认类型的数据绑定

1. 引入项目需要的依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>_20230526</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

2.配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

3.spring-mvc.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:content="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">

    <content:component-scan base-package="cn.hdc.controller"></content:component-scan>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/static/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

4.UserController

package cn.hdc.controller;

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

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@Controller
public class UserController {
    @RequestMapping("/findById")
    public String findById(HttpServletRequest request,
                           HttpServletResponse response,
                           HttpSession session,
                           Model model) {
        System.out.println(response);
        System.out.println(request);
        //向模型中存放数据
        model.addAttribute("msg", "你好");
        //获取请求中参数
        String userid = request.getParameter("userid");
        System.out.println("根据ID查询用户信息" + userid);
        return "success";
    }
}

5.success.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/5/26
  Time: 18:31
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>hello spring-mvc</h1>
${msg}
</body>
</html>

6.运行结果:

 

 

简单数据类型绑定

简单数据类型绑定的概念

简单数据类型的绑定,就是指Java中基本类型(如int、double、String等)的数据绑定。在Spring MVC中进行简单类型的数据绑定,只需客户端请求参数的名称和处理器的形参名称一致即可,请求参数会自动映射并匹配到处理器的形参完成数据绑定.

案例演示简单数据类型的数据绑定

此案例在上一个案例的基础上修改:

1.UserController中添加方法getUserNameAndId

 

2.运行结果:

 

参数别名的设置 

需要注意的是,有时候客户端请求中参数名称和处理器的形参名称不一致,这就会导致处理器无法正确绑定并接收到客户端请求中的参数。为此,Spring MVC提供了@RequestParam注解来定义参数的别名,完成请求参数名称和处理器的形参名称不一致时的数据绑定。

@RequestParam注解的属性

@RequestParam注解的使用 

代码示例:

  

运行结果1:

 

 运行结果2:

 

@PathVariable注解的两个常用属性

当请求的映射方式是REST风格时,上述对简单类型数据绑定的方式就不适用了。为此,Spring MVC提供了@PathVariable注解,通过@PathVariable注解可以将URL中占位符参数绑定到处理器的形参中。@PathVariable注解有以下两个常用属性。

  • value :用于指定URL中占位符名称。
  • required:是否必须提供占位符,默认值为true。

@PathVariable注解的使用 

代码示例:

 

运行结果:

 

 

@PathVariable注解value属性可省略的情况

POJO绑定

POJO数据绑定的使用场景

在使用简单数据类型绑定时,可以很容易的根据具体需求来定义方法中的形参类型和个数,然而在实际应用中,客户端请求可能会传递多个不同类型的参数数据,如果还使用简单数据类型进行绑定,那么就需要手动编写多个不同类型的参数,这种操作显然比较繁琐。为解决这个问题,可以使用POJO类型进行数据绑定。

POJO数据绑定的概念

POJO类型的数据绑定就是将所有关联的请求参数封装在一个POJO中,然后在方法中直接使用该POJO作为形参来完成数据绑定。

代码案例:

1.创建实体类User

package cn.hdc.pojo;

public class User {
    private String username;
    private String password;

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

2.UserController中创建registerUser方法

package cn.hdc.controller;

import cn.hdc.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@Controller
public class UserController {
    @RequestMapping("/findById")
    public String findById(HttpServletRequest request,
                           HttpServletResponse response,
                           HttpSession session,
                           Model model) {
        System.out.println(response);
        System.out.println(request);
        //向模型中存放数据
        model.addAttribute("msg", "你好");
        //获取请求中参数
        String userid = request.getParameter("userid");
        System.out.println("根据ID查询用户信息" + userid);
        return "success";
    }

    @RequestMapping("/get")
    public String getUserNameAndId(String username, Integer id) {
        System.out.println("用户名:" + username + " ID:" + id);
        return "success";
    }

    @RequestMapping("/getUsername")
    public String getUsername(@RequestParam(value = "name", defaultValue = "hdc") String username) {
        System.out.println("用户名:" + username);
        return "success";
    }

    @RequestMapping("/user/{name}")
    public String getPathVariable(@PathVariable("name") String username) {
        System.out.println(username);
        return "success";
    }

    @RequestMapping("/registerUser")
    public String registerUser(User user) {
        System.out.println(user);
        return "success";
    }
}

 register.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/5/26
  Time: 19:44
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>注册</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/registerUser"
      method="post">
    用户名:<input type="text" name="username"/><br/>
    密&nbsp;&nbsp;&nbsp;码:<input type="password" name="password"/>
    <br/>
    <input type="submit" value="注册"/>
</form>
</body>
</html>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--    乱码过滤器-->
    <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>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

spring-mvc.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:content="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">

    <content:component-scan base-package="cn.hdc.controller"></content:component-scan>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/static/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

项目结构:

 

POJO绑定时参数名称问题

在POJO类型数据绑定时,客户端请求的参数名称(本例中指form表单内各元素name的属性值)必须与要绑定的POJO类中的属性名称保持一致。这样客户端发送请求时,请求数据才会自动绑定到处理器形参POJO对象中,否则处理器参数接收的值为null。

运行结果:

 

 

 

解决请求参数中的中文乱码问题

上述代码中,在<filter>元素中,首先使用<fillter-class>元素配置了编码过滤器类org.springframework.web.filter.CharacterEncodingFilter,然后使用<init-param>元素设置统一的编码为UTF-8。最后配置<filter-mapping>元素,拦截前端页面中的所有请求,并交由名称为CharacterEncodingFilter的编码过滤器类进行处理,将所有的请求信息内容以UTF-8的编码格式进行解析。 

------------------------------------------------------------------------------------------

以上可以解决post请求乱码问题,对于get请求中文参数出现乱码,可以在使用参数之前重新编码,如String username = new String(user.getUsername().getBytes("ISO8859-1” ),"UTF-8” );,其中ISO8859-1是Tomcat默认编码,需要将Tomcat编码后的内容再按UTF-8编码。

 

自定义类型转换器

自定义类型转换器使用场景

Spring MVC默认提供了一些常用的类型转换器,这些类型转换器,可以将客户端提交的参数自动转换为处理器形参类型的数据。然而默认类型转换器并不能将提交的参数转换为所有的类型。此时,就需要开发者自定义类型转换器,来将参数转换为程序所需要的类型。

Converter接口的使用 

Spring框架提供了org.springframework.core.convert.converter.Converter接口作为类型转换器,开发者可以通过实现Converter接口来自定义类型转换器。Converter接口的代码如下所示。

在上述代码中,泛型参数中的S表示源类型,T表示目标类型,而convert()方法将源类型转换为目标类型返回,方法内的具体转换规则可由开发者自行定义。 

代码示例:

1.DateConverter

package cn.hdc.convert;

import org.springframework.core.convert.converter.Converter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String s) {
        String dateFormatPattern = "yyyy-MM-dd";
        SimpleDateFormat dateFormat = new SimpleDateFormat(dateFormatPattern);
        Date date = null;
        try {
            date = dateFormat.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
            System.out.println("请采用正确的格式:" + dateFormatPattern);
        }
        return date;
    }
}

 2.spring-mvc.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:content="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd ">

    <content:component-scan base-package="cn.hdc.controller"></content:component-scan>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/static/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--    基于XML配置自定义转换器实现-->
    <!--    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">-->
    <!--        <property name="converters">-->
    <!--            <array>-->
    <!--                <bean class="cn.hdc.convert.DateConverter"></bean>-->
    <!--            </array>-->
    <!--        </property>-->
    <!--    </bean>-->

    <!--    <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>-->


    <!--    基于注解自定义转换器实现-->
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>

 3.UserController

package cn.hdc.controller;

import cn.hdc.pojo.User;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Date;

@Controller
public class UserController {
    @RequestMapping("/findById")
    public String findById(HttpServletRequest request,
                           HttpServletResponse response,
                           HttpSession session,
                           Model model) {
        System.out.println(response);
        System.out.println(request);
        //向模型中存放数据
        model.addAttribute("msg", "你好");
        //获取请求中参数
        String userid = request.getParameter("userid");
        System.out.println("根据ID查询用户信息" + userid);
        return "success";
    }

    @RequestMapping("/get")
    public String getUserNameAndId(String username, Integer id) {
        System.out.println("用户名:" + username + " ID:" + id);
        return "success";
    }

    @RequestMapping("/getUsername")
    public String getUsername(@RequestParam(value = "name", defaultValue = "hdc") String username) {
        System.out.println("用户名:" + username);
        return "success";
    }

    @RequestMapping("/user/{name}")
    public String getPathVariable(@PathVariable("name") String username) {
        System.out.println(username);
        return "success";
    }

    @RequestMapping("/registerUser")
    public String registerUser(User user) {
        System.out.println(user);
        return "success";
    }
//    基于XML配置自定义转换器实现
//    @RequestMapping("/getBirthday")
//    public String getBirthday(Date birthday) {
//        System.out.println(birthday);
//        return "success";
//    }

    //    使用@DateTimeFormat注解完成日期类型的格式转换无需自定义转换器
    @RequestMapping("/getBirthday")
    public String getBirthday(@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday) {
        System.out.println(birthday);
        return "success";
    }
}

4.其余代码同上:

项目结构:

运行结果:

 

日期类型的格式转换

在上述案例中,日期类型的格式转换是基于XML配置自定义转换器实现的。除了XML方式之外,还可以通过@DateTimeFormat注解来简化日期类型的格式转换。使用@DateTimeFormat注解完成日期类型的格式转换无需自定义转换器,也无需在配置文件中定义转换器工厂或格式化工厂,只需将@DateTimeFormat定义在方法的形参前面或成员变量上方,就可以为当前参数或变量指定类型转换规则。

数组绑定

数组绑定的使用场景

在实际开发中,可能会遇到客户端请求需要传递多个同名参数到服务器端的情况,这种情况采用前面讲解的简单数据绑定的方式显然是不合适的。此时,可以使用数组来接收客户端的请求参数,完成数据绑定。

代码示例:

1.ProductController

package cn.hdc.controller;

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

@Controller
public class ProductController {
    @RequestMapping("/getProducts")
    public String getProducts(String[] proIds) {
        for (String proId : proIds) {
            System.out.println("商品的id为:" + proId);
        }
        return "success";
    }
}

 2.Product

package cn.hdc.pojo;

public class Product {
    private String proId;
    private String proName;

    @Override
    public String toString() {
        return "Product{" +
                "proId='" + proId + '\'' +
                ", proName='" + proName + '\'' +
                '}';
    }

    public String getProId() {
        return proId;
    }

    public void setProId(String proId) {
        this.proId = proId;
    }

    public String getProName() {
        return proName;
    }

    public void setProName(String proName) {
        this.proName = proName;
    }
}

 3.product.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/5/26
  Time: 21:53
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>提交商品</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/getProducts" method="post">
    <table width="220px" border="1">
        <tr>
            <td>选择</td>
            <td>商品名称</td>
        </tr>
        <tr>
            <td>
                <input name="proIds" value="1" type="checkbox">
            </td>
            <td>Java基础教程</td>
        </tr>
        <tr>
            <td>
                <input name="proIds" value="2" type="checkbox">
            </td>
            <td>JavaWeb案例</td>
        </tr>
        <tr>
            <td>
                <input name="proIds" value="3" type="checkbox">
            </td>
            <td>SSM框架实战</td>
        </tr>
    </table>
    <input type="submit" value="提交商品"/>
</form>
</body>
</html>

4.其他代码和上述一样

项目结构:

运行结果:

 

 

 

 

集合绑定

集合绑定的使用

集合中存储简单类型数据时,数据的绑定规则和数组的绑定规则相似,需要请求参数名称与处理器的形参名称保持一致。不同的是,使用集合绑定时,处理器的形参名称需要使用@RequestParam注解标注。

代码示例:

把ProductController中的第一个方法复制一份,使用注解进行集合绑定

 

运行结果:

 

 

 

 

@RequestParam注解解决集合绑定的异常问题

如果getProducts()方法中不使用@RequestParam注解,Spring MVC默认将List作为对象处理,赋值前先创建List对象,然后将proIds作为List对象的属性进行处理。由于List是接口,无法创建对象,所以会出现无法找到构造方法异常。如果将类型更改为可创建对象的类型,如ArrayList,可以创建ArrayList对象,但ArrayList对象依旧没有prolds属性,因此无法正常绑定,数据为空。此时需要告知Spring MVC的处理器prolds是一组数据,而不是一个单一数据。通过@RequestParam注解,将参数打包成参数数组或集合后,Spring MVC才能识别该数据格式,并判定形参类型是否为数组或集合,并按数组或集合对象的形式操作数据。

 

复杂POJO绑定

复杂POJO数组绑定的使用场景

使用简单POJO类型已经可以完成多数的数据绑定,但有时客户端请求中传递的参数比较复杂。例如,在用户查询订单时,页面传递的参数可能包括订单编号、用户名称等信息,这就包含了订单和用户两个对象的信息。如果将订单和用户的所有查询条件都封装在一个简单POJO中,显然会比较混乱,这时可以考虑使用复杂POJO类型的数据绑定。

复杂POJO的3种属性的类型

所谓的复杂POJO,就是POJO属性的类型不止包含简单数据类型,还包含对象类型、List类型和Map类型等其他引用类型。接下来分别对复杂POJO中属性为对象类型的数据绑定、属性为List类型的数据绑定和属性为Map类型的数据绑定进行讲解。

属性为对象类型的数据绑定 

1.创建一个订单类Order,用于封装订单信息

package cn.hdc.pojo;

public class Order {
    private String orderId;

    @Override
    public String toString() {
        return "Order{" +
                "orderId='" + orderId + '\'' +
                '}';
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }
}

 2.修改User.java类,在User类中新增Order类型的属性order,并定义相应的getter和setter方法。修改后User类的具体代码如下所示。

package cn.hdc.pojo;

public class User {
    private String username;
    private String password;

    private Order order;

    public Order getOrder() {
        return order;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", order=" + order +
                '}';
    }

    public void setOrder(Order order) {
        this.order = order;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

3.在UserController.java类中定义方法findOrderWithUser( ),用于获取客户端请求中的User信息,findOrderWithUser()方法的具体代码如下所示。

package cn.hdc.controller;

import cn.hdc.pojo.User;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Date;

@Controller
public class UserController {
    @RequestMapping("/findById")
    public String findById(HttpServletRequest request,
                           HttpServletResponse response,
                           HttpSession session,
                           Model model) {
        System.out.println(response);
        System.out.println(request);
        //向模型中存放数据
        model.addAttribute("msg", "你好");
        //获取请求中参数
        String userid = request.getParameter("userid");
        System.out.println("根据ID查询用户信息" + userid);
        return "success";
    }

    @RequestMapping("/get")
    public String getUserNameAndId(String username, Integer id) {
        System.out.println("用户名:" + username + " ID:" + id);
        return "success";
    }

    @RequestMapping("/getUsername")
    public String getUsername(@RequestParam(value = "name", defaultValue = "hdc") String username) {
        System.out.println("用户名:" + username);
        return "success";
    }

    @RequestMapping("/user/{name}")
    public String getPathVariable(@PathVariable("name") String username) {
        System.out.println(username);
        return "success";
    }

    @RequestMapping("/registerUser")
    public String registerUser(User user) {
        System.out.println(user);
        return "success";
    }
//    基于XML配置自定义转换器实现
//    @RequestMapping("/getBirthday")
//    public String getBirthday(Date birthday) {
//        System.out.println(birthday);
//        return "success";
//    }

    //    使用@DateTimeFormat注解完成日期类型的格式转换无需自定义转换器
    @RequestMapping("/getBirthday")
    public String getBirthday(@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday) {
        System.out.println(birthday);
        return "success";
    }

    @RequestMapping("/findOrderWithUser")
    public String findOrderWithUser(User user) {
        System.out.println("用户民" + user.getUsername());
        System.out.println("订单号" + user.getOrder().getOrderId());
        return "success";
    }
}

4.order.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/5/26
  Time: 22:53
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>订单信息</title></head>
<body>
<form action="${pageContext.request.contextPath}/findOrderWithUser" method="post">
    所属用户:<input type="text" name="username"/> <br/>
    订单编号:<input type="text" name="order.orderId"/><br/><input type="submit" value="查询"/>
</form>
</body>
</html>

5.项目结构:

 

运行结果:

 

 

 复杂POJO对象绑定的格式

在复杂POJO数据绑定时,如果数据需要绑定到POJO属性对象的属性中,客户端请求的参数名(本例中指form表单内各元素name的属性值)的格式必须为“属性对象名称.属性”,其中“属性对象名称”要和POJO的属性对象名称一致,“属性”要和属性对象所属类的属性一致。

属性为List类型的数据绑定 

代码示例:

1.修改User.java类,将User类中订单属性修改为List类型。由于用户一般拥有多个收货地址,在User类中新增List类型的地址属性。

package cn.hdc.pojo;

import java.util.List;

public class User {
    private String username;
    private String password;
    private List<Order> orders;
    private List<String> address;

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", orders=" + orders +
                ", address=" + address +
                '}';
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public List<Order> getOrders() {
        return orders;
    }

    public void setOrders(List<Order> orders) {
        this.orders = orders;
    }

    public List<String> getAddress() {
        return address;
    }

    public void setAddress(List<String> address) {
        this.address = address;
    }

    //    private Order order;

//    public Order getOrder() {
//        return order;
//    }
//
//    public void setOrder(Order order) {
//        this.order = order;
//    }

}

 2.创建一个订单处理器类OrderController,在OrderController类中定义showOrders()方法,用于展示用户的订单信息。

package cn.hdc.controller;

import cn.hdc.pojo.Order;
import cn.hdc.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Controller
public class OrderController {
    @RequestMapping("/showOrders")
    public String showOrders(User user) {
        List<String> address = user.getAddress();
        List<Order> orders = user.getOrders();

        for (int i = 0; i < orders.size(); i++) {
            Order order = orders.get(i);
            String addr = address.get(i);
            System.out.println("订单编号:" + order.getOrderId() + "订单名称:" + order.getOrderName() + " 配送地址:" + addr);
        }
        return "success";
    }
}

3.创建一个订单信息文件orders.jsp,在orders.jsp中创建一个表单用于提交用户的订单信息。表单提交时,表单数据分别圭装到User的订单属性orders和地址属性address中。

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/5/26
  Time: 23:12
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>

<head>
    <title>订单信息</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/showOrders" method="post">
    <table width="220px" border="1">
        <tr>
            <td>订单号</td>
            <td>订单名称</td>
            <td>配送地址</td>
        </tr>
        <tr>
            <td>
                <input name="orders[0].orderId" value="1" type="text">
            </td>
            <td>
                <input name="orders[0].orderName" value="Java基础教程" type="text">
            </td>
            <td><input name="address" value="北京海淀" type="text"></td>
        </tr>
        <tr>
            <td>
                <input name="orders[1].orderId" value="2" type="text">
            </td>
            <td>
                <input name="orders[1].orderName" value="JavaWeb案例" type="text">
            </td>
            <td><input name="address" value="北京昌平" type="text"></td>
        </tr>
        <tr>
            <td>
                <input name="orders[2].orderId" value="3" type="text">
            </td>
            <td>
                <input name="orders[2].orderName" value="SSM框架实战" type="text">
            </td>
            <td><input name="address" value="北京朝阳" type="text"></td>
        </tr>
    </table>
    <input type="submit" value="订单信息"/>
</form>
</body>
</html>

 4.项目结构:

运行结果:

 

复杂POJO数组绑定的编写要求 

在复杂POJO数据绑定时,如果数据绑定到List类型的属性,客户端请求的参数名称(本例中指form表单内各元素name的属性值)编写必须符合以下要求。

  1. 如果List的泛型为简单类型,则客户端参数名称必须和POJO类中List属性所属类中的属性名称保持一致。
  2. 如果List的泛型参数为对象类型,则客户端参数名称必须与POJO类的层次结构名称保持一致,并使用数组格式描述对象在List中的位置,即客户端参数名称必须和最终绑定在List中的某个对象的某个属性的名称保持一致。

属性为Map类型的数据绑定 

代码示例:

1.修改Order.java类,在Order类中新增HashMap类型的属性productInfo,用于封装订单中的商品信息,其中productInfo的键用来存放商品的类别,productInfo的值用来存放商品类别对应的商品。

package cn.hdc.pojo;

import java.util.HashMap;

public class Order {
    private String orderId;

    private HashMap<String, Product> productInfo;

    @Override
    public String toString() {
        return "Order{" +
                "orderId='" + orderId + '\'' +
                ", productInfo=" + productInfo +
                '}';
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public HashMap<String, Product> getProductInfo() {
        return productInfo;
    }

    public void setProductInfo(HashMap<String, Product> productInfo) {
        this.productInfo = productInfo;
    }
}

2.修改OrderController.java类,在OrderController类中新增getOrderInfo()方法,用于获取客户端提交的订单信息,并将获取到的订单信息打印在控制台。getOrderInfo()方法的具体代码如下所示。

package cn.hdc.controller;

import cn.hdc.pojo.Order;
import cn.hdc.pojo.Product;
import cn.hdc.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.HashMap;
import java.util.List;
import java.util.Set;

@Controller
public class OrderController {
//    @RequestMapping("/showOrders")
//    public String showOrders(User user) {
//        List<String> address = user.getAddress();
//        List<Order> orders = user.getOrders();
//
//        for (int i = 0; i < orders.size(); i++) {
//            Order order = orders.get(i);
//            String addr = address.get(i);
//            System.out.println("订单编号:" + order.getOrderId() + "订单名称:" + order.getOrderName() + " 配送地址:" + addr);
//        }
//        return "success";
//    }

    @RequestMapping("/orderInfo")
    public String getOrderInfo(Order order) {
        System.out.println("订单编号:" + order.getOrderId());
        HashMap<String, Product> productInfo = order.getProductInfo();
        Set<String> keys = productInfo.keySet();
        for (String type : keys) {
            Product product = productInfo.get(type);
            System.out.println("类型:" + type + "商品名称" + product.getProName());
        }
        return "success";
    }
}

 3.创建一个订单信息页面order_info.jsp,在order_info.jsp中创建一个表单用于提交订单信息。表单提交时,表单数据分别封装到Order的orderId属性和商品信息属性productInfo中。

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/5/26
  Time: 23:45
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>订单信息</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/orderInfo" method="post">
    <table border="1">
        <tr>
            <td colspan="2">
                订单id:<input type="text" name="orderId" value="1">
            </td>
        </tr>
        <tr>
            <td>商品Id</td>
            <td>商品名称</td>
        </tr>
        <tr>
            <td>
                <input name="productInfo['生鲜'].proId" value="1" type="text">
            </td>
            <td>
                <input name="productInfo['生鲜'].proName" value="三文鱼" type="text">
            </td>
        </tr>
        <tr>
            <td>
                <input name="productInfo['酒水'].proId" value="2" type="text">
            </td>
            <td>
                <input name="productInfo['酒水'].proName" value="红牛" type="text">
            </td>
        </tr>
    </table>
    <input type="submit" value="提交"/>
</form>
</body>
</html>

4.项目结构:

运行结果:

 

 

  

数据绑定到Map类型的属性时的参数命名要求

在复杂POJO数据绑定时,如果数据绑定到Map类型的属性,客户端请求的参数名称(本例中指form表单内各元素name的属性值)必须与POJO类的层次结构名称保持一致,并使用键值的映射格式描述对象在Map中的位置,即客户端参数名称必须和要绑定的Map中的具体对象的具体属性的名称保持一致。

JSON数据绑定

1.引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>_20230526</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!--Jackson转换核心包依赖-->
        <dependency>
            <groupId>
                com.fasterxml.jackson.core
            </groupId>
            <artifactId>
                jackson-core
            </artifactId>
            <version>
                2.9.2
            </version>
        </dependency>
        <!--Jackson转换的数据绑定包依赖-->
        <dependency>
            <groupId>
                com.fasterxml.jackson.core
            </groupId>
            <artifactId>
                jackson-databind
            </artifactId>
            <version>
                2.9.2
            </version>
        </dependency>
        <!--Jackson JSON转换注解包-->
        <dependency>
            <groupId>
                com.fasterxml.jackson.core
            </groupId>
            <artifactId>
                jackson-annotations
            </artifactId>
            <version>
                2.9.0
            </version>
        </dependency>
    </dependencies>
</project>

2.在项目中导入jQuery文件

3.创建一个商品信息页面products.jsp,在products.jsp中创建一个表单用于填写商品信息,表单提交时,表单发送异步请求将表单的商品信息发送到处理器。 

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/5/27
  Time: 0:15
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>异步提交商品</title>
    <script type="text/javascript" src="${pageContext.request.contextPath }/static/js/jquery.js"></script>
</head>
<body>
<form id="products">
    <table border="1">
        <tr>
            <th>商品id</th>
            <th>商品名称</th>
            <th>提交</th>
        </tr>
        <tr>
            <td>
                <input name="proId" value="1" id="proId" type="text">
            </td>
            <td><input name="proName" value="三文鱼" id="proName" type="text"></td>
            <td><input type="button" value="提交单个商品" onclick="sumbmitProduct()"></td>
        </tr>
        <tr>
            <td><input name="proId" value="2" id="proId2" type="text"></td>
            <td><input name="proName" value="红牛" id="proName2" type="text"></td>
            <td><input type="button" value="提交多个商品" onclick="submitProducts()"></td>
        </tr>
    </table>
</form>

<script type="text/javascript">
    function sumbmitProduct() {
        var proId = $("#proId").val();
        var proName = $("#proName").val();
        $.ajax({
            url: "${pageContext.request.contextPath }/getProduct",
            type: "post",
            data: JSON.stringify({proId: proId, proName: proName}),
            contentType: "application/json;charset=UTF-8",
            dataType: "json",
            success: function (response) {
                alert(response);
            }
        });
    }

    function submitProducts() {
        var pro1 = {proId: $("#proId").val(), proName: $("#proName").val()}
        var pro2 = {proId: $("#proId2").val(), proName: $("#proName2").val()}
        $.ajax({
            url: "${pageContext.request.contextPath }/getProductList",
            type: "post",
            data: JSON.stringify([pro1, pro2]),
            contentType: "application/json;charset=UTF-8",
            dataType: "json",
            success: function (response) {
                alert(response);
            }
        });
    }
</script>
</body>
</html>

 4.修改ProductController.java类,在ProductController类中新增getProduct()方法,用于获取客户端提交的单个商品信息。在ProductController类中新增getProductList()方法,用于获取客户端提交的多个商品信息。

package cn.hdc.controller;

import cn.hdc.pojo.Product;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

@Controller
public class ProductController {
//    @RequestMapping("/getProducts")
//    public String getProducts(String[] proIds) {
//        for (String proId : proIds) {
//            System.out.println("商品的id为:" + proId);
//        }
//        return "success";
//    }

//    @RequestMapping("/getProducts")
//    public String getProducts(@RequestParam("proIds") List<String> proIds) {
//        for (String proId : proIds) {
//            System.out.println("商品的id为:" + proId);
//        }
//        return "success";
//    }

    @RequestMapping("/getProduct")
    public String getProduct(@RequestBody Product product) {
        System.out.println("单个商品:" + product);
        return "success";
    }

    @RequestMapping("/getProductList")
    public String getProductList(@RequestBody List<Product> productList) {
        System.out.println("多个商品:");
        productList.forEach(product -> {
            System.out.println(product);
        });
        return "success";
    }
}

5.spring-mvc.xml:在项目的web.xml文件中配置的DispatcherServlet会拦截所有URL,导致项目中的静态资源(如css、jsp、 js等)也被DispatcherServlet拦截。如果想放行静态资源可以在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:content="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd ">

    <content:component-scan base-package="cn.hdc.controller"></content:component-scan>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/static/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!--    基于XML配置自定义转换器实现-->
    <!--    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">-->
    <!--        <property name="converters">-->
    <!--            <array>-->
    <!--                <bean class="cn.hdc.convert.DateConverter"></bean>-->
    <!--            </array>-->
    <!--        </property>-->
    <!--    </bean>-->

    <!--    <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>-->


    <!--    基于注解自定义转换器实现-->
    <mvc:annotation-driven></mvc:annotation-driven>

    <mvc:resources mapping="/static/js/**" location="/static/js/"></mvc:resources>

<!--    <mvc:default-servlet-handler></mvc:default-servlet-handler>-->
</beans>

 6.项目结构:(注意用的是products.jsp)

运行结果:

 

从图中所示的打印信息可以得出,客户端异步提交的JSON数据,按照形参product属性的格式进行关联映射,并赋值给product对应的属性,完成了JSON数据的绑定。

<mvc:resources .../>的两个重要属性

JSON转换器配置和静态资源访问配置

JSON转换器配置和静态资源访问配置,除了之前讲解的配置方案之外,还可以通过其他方式完成,下面讲解两种配置方式,使用<bean>元素配置JSON转换器和静态资源访问的配置方式。

使用<bean>元素配置JSON转换器 

在配置JSON转换器时,除了常用的<mvc:annotation-driven />元素,还可以使用<bean>元素进行显示的配置,<bean>元素配置JSON转换器方式具体如下所示。

 

静态资源访问的配置方式

 

页面跳转 

返回值为void类型的页面跳转到默认页面

当Spring MVC方法的返回值为void类型,方法执行后会跳转到默认的页面。默认页面的路径由方法映射路径和视图解析器中的前缀、后缀拼接成,拼接格式为“前缀+方法映射路径+后缀”。如果Spring MVC的配置文件中没有配置视图解析器,则会报HTTP Status 500错误。

代码示例:

1.创建一个页面跳转类PageController,在PageController类中定义方法showPageByVoid(),用于测试Spring MVC方法返回值为void的页面跳转。

package cn.hdc.controller;

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

@Controller
public class PageController {
    @RequestMapping("/register")
    public void showPageByVoid() {
        System.out.println("showPageByVoid running");
    }
}

项目结构:

 

 运行结果:

访问地址后,执行了showPageByVoid()方法,并且在方法执行后成功跳转到static文件夹下的register.jsp页面。页面虽然跳转了,但是浏览器地址栏没有变化,原因是Spring MVC对请求默认按转发的方式进行响应。 

反回值为String类型,不携带数据的页面跳转

代码示例:

1.修改文件PageController.java,新增showPageByString()方法,用于测试返回值为String类型的页面跳转,showPageByString()方法的实现代码如下所示。

package cn.hdc.controller;

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

@Controller
public class PageController {
    @RequestMapping("/register")
    public void showPageByVoid() {
        System.out.println("showPageByVoid running");
    }

    @RequestMapping("/showPageByString")
    public String showPageByString() {
        System.out.println("showPageByString running");
        return "register";
    }

    @RequestMapping("/showPageByForward")
    public String showPageByForward() {
        System.out.println("showPageByForward running");
//        视图解析器不在生效
        return "forward:static/order.jsp";
    }

    @RequestMapping("/showPageByRedirect")
    public String showPageByRedirect() {
        System.out.println("showPageByRedirect running");
        //        视图解析器不在生效
        return "redirect:https://www.baidu.com";
    }
}

 项目结构:

运行结果:

 

 

 

 

返回值为String类型的页面跳转的转发方式 

当方法的返回值为普通的字符串时,Spring MVC在方法执行后会默认以转发的方式响应给客户端。除了这种默认的转发方式,还可以返回指定前缀的字符串,来设定处理器执行后对请求进行转发还是重定向,设定转发和重定向的字符串格式如下所示。

  • forward:需要转发到的资源路径
  • redirect:需要重定向到的资源路径

注意:方法返回的字符串一旦添加了“forward:”或“redirect:”前缀,那么视图解析器不再会为
方法返回值拼接前缀和后缀了。 

目标:

掌握返回值为String类型的页面跳转-携带数据,能够在程序中使用String返回值类型进行页面跳转 

携带数据

在此之前,本章所有转发的案例都只是直接跳转到页面,并未在转发时携带数据到页面。在实际开发中,在转发时常常需要携带数据。在讲解Spring MVC的数据绑定时,展示了SpringMVC默认支持的数据类型,在转发时也可以通过这些默认类型的对象完成数据的携带。接下来通过一个案例演示携带数据的页面转发,该案例使用HttpServletRequest类型形参和Model类型形参进行数据传递。 

代码示例: 

1.修改文件PageController.java,新增showPageByRequest()方法和showPageByModel()方法。

package cn.hdc.controller;

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

import javax.servlet.http.HttpServletRequest;

@Controller
public class PageController {
    @RequestMapping("/register")
    public void showPageByVoid() {
        System.out.println("showPageByVoid running");
    }

    @RequestMapping("/showPageByString")
    public String showPageByString() {
        System.out.println("showPageByString running");
        return "register";
    }

    @RequestMapping("/showPageByForward")
    public String showPageByForward() {
        System.out.println("showPageByForward running");
//        视图解析器不在生效
        return "forward:static/order.jsp";
    }

    @RequestMapping("/showPageByRedirect")
    public String showPageByRedirect() {
        System.out.println("showPageByRedirect running");
        //        视图解析器不在生效
        return "redirect:https://www.baidu.com";
    }

    @RequestMapping("/showPageByRequest")
    public String showPageByRequest(HttpServletRequest request) {
        System.out.println("showPageByRequest running");
        request.setAttribute("username", "Tom");
        return "register";
    }

    @RequestMapping("/showPageByModel")
    public String showPageByModel(Model model) {
        System.out.println("showPageByModel running");
        model.addAttribute("username", "Tom");
        return "register";
    }
}

2.在register.jsp的表单中添加value属性,用于接收转发传递过来的数据。

运行结果:

 

 

 

返回值为ModelAndView类型的页面跳转 

ModelAndView对象组成部分

使用方法的返回值可以设定跳转的逻辑视图名称,使用Model等对象实现页面跳转时传输数据。除此之外,Spring MVC还提供了兼顾视图和数据的对象ModelAndView ,ModelAndView对象包含视图相关内容和模型数据两部分,其中视图相关的内容可以设置逻辑视图的名称,也可以设置具体的View实例﹔模型数据则会在视图渲染过程中被合并到最终的视图输出。

ModelAndView设置视图和数据模型的方法 

ModelAndView方法说明 

setViewName()方法和setView()方法都是为ModelAndView对象设置视图的方法,其中前者使用更方便,因此setViewName()方法比较常用。后3个方法都是向ModelAndView对象中添加模型数据的,其中addObject(Object attributeValue)方法添加的attributeValue,默认名称为attributeValue类型全限定名的最后一个单词且首字母小写;addObject(String attributeName, Object attributeValue)方法可以在页面上以${attributeName}方式取出attributeValue。

代码示例:

1.修改文件PageController.java,新增showModelAndView()方法,在showModelAndView()方法中使用ModelAndView封装数据和视图,完成页面跳转时传递数据。

package cn.hdc.controller;

import cn.hdc.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

@Controller
public class PageController {
    @RequestMapping("/register")
    public void showPageByVoid() {
        System.out.println("showPageByVoid running");
    }

    @RequestMapping("/showPageByString")
    public String showPageByString() {
        System.out.println("showPageByString running");
        return "register";
    }

    @RequestMapping("/showPageByForward")
    public String showPageByForward() {
        System.out.println("showPageByForward running");
//        视图解析器不在生效
        return "forward:static/order.jsp";
    }

    @RequestMapping("/showPageByRedirect")
    public String showPageByRedirect() {
        System.out.println("showPageByRedirect running");
        //        视图解析器不在生效
        return "redirect:https://www.baidu.com";
    }

    @RequestMapping("/showPageByRequest")
    public String showPageByRequest(HttpServletRequest request) {
        System.out.println("showPageByRequest running");
        request.setAttribute("username", "Tom");
        return "register";
    }

    @RequestMapping("/showPageByModel")
    public String showPageByModel(Model model) {
        System.out.println("showPageByModel running");
        model.addAttribute("username", "Tom");
        return "register";
    }

    @RequestMapping("/showModelAndView")
    public ModelAndView showModelAndView() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("username", "Tom");
        User user = new User();
        user.setPassword("123456");
        modelAndView.addObject("user", user);
        modelAndView.setViewName("register");
        return modelAndView;
    }
}

2.修改register.jsp

运行结果:

 

数据回写 

代码示例:

1.创建一个数据回写类DataController,在DataController类中定义showDataByResponse()方法,用于测试在Spring MVC中普通字符串的回写。

package cn.hdc.controller;

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

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
public class DataController {
    @RequestMapping("/showDataByResponse")
    public void showDataByResponse(HttpServletResponse response) throws IOException {
        response.getWriter().write("success");
    }
}

运行结果:

JSON数据的回写 

代码示例:

1.修改文件DataController.java,在DataController类中新增showDataByJSON()方法,用于将对象转换成JSON数据并写入输出流中完成回写。

package cn.hdc.controller;

import cn.hdc.pojo.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
public class DataController {
    @RequestMapping("/showDataByResponse")
    public void showDataByResponse(HttpServletResponse response) throws IOException {
        response.getWriter().write("success");
    }

//    @RequestMapping("/showDataByJson")
//    public void showDataByJson(HttpServletResponse response) throws IOException {
//        User user = new User();
//        user.setUsername("tom");
//        user.setPassword("12346");
//        ObjectMapper objectMapper = new ObjectMapper();
//        String json = objectMapper.writeValueAsString(user);
//        response.getWriter().write(json);
//    }

    @RequestMapping("/showDataByJson")
    @ResponseBody
    public void showDataByJson(HttpServletResponse response) throws IOException {
        User user = new User();
        user.setUsername("tom");
        user.setPassword("12346");
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(user);
        response.getWriter().write(json);
    }
}

运行结果:

由图中所示的内容可以得出,访问地址后,执行了showDataByJSON()方法,方法执行后将User对象的数据转换成JSON格式的数据输出到请求页面中了。

 @ResponseBody注解的使用范围

@ResponseBody注解可以标注在方法和类上,当标注在类上时,表示该类中的所有方法均应用@ResponseBody注解。如果需要当前类中的所有方法均应用@ResponseBody注解,也可以使用@RestController注解。

 @ResponseBody注解的2个使用要求

使用@ResponseBody注解,项目至少需要符合2个要求,分别如下所示。项目中有转换JSON相关的依赖。可以配置转换JSON数据的消息类型转换器。针对上述两个要求,该项目都已经满足,项目的pom.xml文件中引入了Jackson相关的依赖,可以用于转换JSON ; Spring MVC的配置文件中配置的<mvc:annotation-driven />元素默认注册了Java数据转JSON数据的消息转换器。

集合数据转换成JSON数据后的回写

代码示例:

1.修改DataController.java,在Datacontroller尖中新增getUser)方法和addProducts()方法,分别用于返回JSON类型的User信息和用于返回JSON类型的Product列表信息。

package cn.hdc.controller;

import cn.hdc.pojo.Product;
import cn.hdc.pojo.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Controller
public class DataController {
    @RequestMapping("/showDataByResponse")
    public void showDataByResponse(HttpServletResponse response) throws IOException {
        response.getWriter().write("success");
    }

//    @RequestMapping("/showDataByJson")
//    public void showDataByJson(HttpServletResponse response) throws IOException {
//        User user = new User();
//        user.setUsername("tom");
//        user.setPassword("12346");
//        ObjectMapper objectMapper = new ObjectMapper();
//        String json = objectMapper.writeValueAsString(user);
//        response.getWriter().write(json);
//    }

    @RequestMapping("/showDataByJson")
    @ResponseBody
    public void showDataByJson(HttpServletResponse response) throws IOException {
        User user = new User();
        user.setUsername("tom");
        user.setPassword("12346");
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(user);
        response.getWriter().write(json);
    }

    @RequestMapping("/getUser")
    @ResponseBody
    public User getUser() {
        User user = new User();
        user.setUsername("tom");
        user.setPassword("12346");
        return user;
    }

    @RequestMapping("/addProducts")
    @ResponseBody
    public List<Product> addProduct() {
        List<Product> productList = new ArrayList<>();
        Product product = new Product();
        product.setProId("p001");
        product.setProName("三文鱼");
        Product product1 = new Product();
        product1.setProId("p002");
        product1.setProName("茅台");
        productList.add(product);
        productList.add(product1);
        return productList;
    }
}

 2.创建一个商品添加页面product_add.jsp,在product_add.jsp中创建一个表格,用于显示用户信息和添加商品信息。product_add.jsp的部分代码如下所示。

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2023/5/27
  Time: 2:20
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>

<head>
    <title>商品添加</title>
    <script type="text/javascript" src="${pageContext.request.contextPath }/static/js/jquery.js"></script>
</head>
<body>
<table id="products" border="1" width="60%">
    <tr align="center">
        <td>欢迎您:</td>
        <td id="username"></td>
    </tr>
    <tr align="center">
        <td colspan="2" align="center">
            <input type="button" value="添加多个商品" onclick="addProducts()">
        </td>
    </tr>
    <tr align="center">
        <td>商品id</td>
        <td>商品名称</td>
    </tr>
</table>
<script type="text/javascript">
    //显示当前用户名
    window.onload = function () {
        var url = "${pageContext.request.contextPath }/getUser";
        $.get(url, function (response) {
//将处理器返回的用户信息中的用户名显示在表格中
            $("#username").text(response.username);
        })
    }

    //添加商品
    function addProducts() {
        var url = "${pageContext.request.contextPath }/addProducts";
        $.get(url, function (products) {
//将处理器返回的商品列表信息添加到表格中
            for (var i = 0; i < products.length; i++) {
                $("#products").append("<tr><td>" + products[i].proId + "</td><td>" + products[i].proName + "</td></tr>");
            }
        })
    }
</script>
</body>
</html>

 运行结果:

由图中所示的内容可以得出,页面加载完,页面异步将用户的信息显示在单元格中,成功回写了User对象信息对应的JSON数据。单击上图所示的“添加多个商品”按钮,程序成功回写了List对应的JSON数据。 

项目地址

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
MVC(Model-View-Controller)是一种软件设计模式,它将应用程序分为三个部分:模型(Model)、视图(View)和控制器(Controller)。这种模式的目的是使应用程序的不同部分之间的职责更加清晰,从而提高代码的可维护性和可扩展性。 在MVC中,模型表示应用程序数据和业务逻辑,视图显示数据和用户界面,控制器接收和处理用户输入,并更新模型和视图。这种分离使得修改应用程序的某个部分时,不会影响其他部分的代码。 Spring MVC是一个MVC框架,它基于Java Servlet API实现,并且提供了很多工具和类来简化Web应用程序的开发。Spring MVC的核心是DispatcherServlet,它接收所有的HTTP请求,并将它们分派到相应的控制器进行处理。 要搭建Spring MVC开发环境,需要进行以下步骤: 1. 配置web.xml文件,将DispatcherServlet映射到相应的URL上。 2. 配置Spring MVC配置文件,包括定义控制器、视图解析器、拦截器等。 3. 编写控制器类,处理请求并返回相应的视图。 Spring MVC中请求的响应与请求参数绑定方法有以下几种: 1. @RequestParam注解:将请求参数绑定到控制器方法的参数上。 2. @PathVariable注解:将URL路径变量绑定到控制器方法的参数上。 3. @ModelAttribute注解:将请求参数绑定到控制器方法的JavaBean对象上。 4. @RequestBody注解:将请求体中的数据绑定到控制器方法的参数上。 通过使用这些注解,可以轻松地处理HTTP请求,并将请求参数绑定到控制器方法的参数上,从而简化了开发过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿瞒有我良计15

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值