Spring MVC数据绑定和响应

news2024/10/7 19:35:42

数据绑定

在程序运行时,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数据。 

项目地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/574444.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电力监控系统在中原科技城智慧能源配电工程中的应用

摘 要&#xff1a;随着社会经济的快速发展&#xff0c;我国变电站正朝着现代化的方向不断发展&#xff0c;自动化设备以及继电保护装置凭借自身优异性能而获得广泛应用。本文介绍的AM5SE系列的微机保护装置&#xff0c;可以针对中原科技城智慧能源配电工程中不同保护对象提供对…

真无线蓝牙耳机什么牌子好?盘点五款质量好的蓝牙耳机

相信很多人都有过这样的经历&#xff0c;早 晚高峰像沙丁鱼般被挤在公交或地铁上&#xff0c;嘈杂的环境、工作的劳累让你只想听听音乐追追剧&#xff0c;给自己一些放松的时光。可拿出有线耳机却常常被挤掉&#xff0c;更有被扯到耳朵的时候。想换一款蓝牙耳机&#xff0c;但面…

Kubernetes 证书详解

K8S 证书介绍 在 Kube-apiserver 中提供了很多认证方式&#xff0c;其中最常用的就是 TLS 认证&#xff0c;当然也有 BootstrapToken&#xff0c;BasicAuth 认证等&#xff0c;只要有一个认证通过&#xff0c;那么 Kube-apiserver 即认为认证通过。下面就主要讲解 TLS 认证。 …

chatgpt赋能python:Python动作捕捉:何为动作捕捉及其应用

Python动作捕捉&#xff1a;何为动作捕捉及其应用 介绍 动作捕捉是一种技术&#xff0c;可将人或物体的运动转换为数字形式。在过去的几十年里&#xff0c;动作捕捉已被广泛应用于电影制作、游戏开发、医学研究等领域。 Python是一种功能强大的编程语言&#xff0c;已成为许…

AI 工具分享第 4 期:13 款国外免费AI视频生成工具

0. 未来百科 未来百科&#xff0c;是一个知名的AI产品导航网站 —— 为发现全球优质AI工具而生 。目前已 聚集全球3000优质AI工具产品 &#xff0c;旨在帮助用户发现全球最好的AI工具&#xff0c;同时为研发AI垂直应用的创业公司提供展示窗口&#xff0c;迎接未来的AI时代。未来…

Revit建模|Revit风管怎么绘制?

​绘制风管是机电工程重要的一环&#xff0c;对于不少刚接触Revit的小伙伴来说似乎还无从下手&#xff0c;今天就让小编来告诉大家在Revit中绘制风管的方法。 一、在Revit绘制风管 第一步&#xff1a;首先我们先在revit的界面中项目文件找到风管。 第二步&#xff1a;打开后我…

医疗IT系统安科瑞隔离电源装置在医院的应用

【摘要】介绍该三级综合医院采用安科瑞隔离电源系统5件套&#xff0c;使用落地式配电柜安装方式&#xff0c;从而实现将TN系统转化为IT系统&#xff0c;以及系统绝缘情况监测。 【关键词】医用隔离电源系统&#xff1b;IT系统&#xff1b;绝缘情况监测&#xff1b;三级综合医院…

tektronix泰克TDS3054数字荧光示波器

tektronix TDS3054是泰克TDS3000系列示波器&#xff0c;它是一种新的图形界面操作模式&#xff0c;称为QuickMenu。这种快速访问的用户界面使得主要的示波器控制访问一个单一的按键。每一个示波器都包含一个在示波器中运行的在线巡览盘。此磁盘提供了产品的操作和功能的概述。 …

Qt与Excel:从底层原理到上层应用的全面探索

Qt与Excel&#xff1a;从底层原理到上层应用的全面探索 一、Qt与Excel文件的交互基础&#xff08;Basics of Qt and Excel Interaction&#xff09;1.1 Qt与Excel文件的基本概念&#xff08;Basic Concepts of Qt and Excel Files&#xff09;1.2 Qt读取Excel文件的基本方法&am…

nignx虚拟主机头配置方法

目录 一、不同ip地址访问 二、不同端口访问 三、不同域名访问 一、不同ip地址访问 步骤一&#xff1a;配置虚拟主机头文件 cd /etc/nginx/conf.d/ 新建一个default2.conf touch default2.conf 配置default.cof 配置如下&#xff1a; Listen 192.168.59.137:80; access_log …

自动清理 ES 历史数据

一、 背景 随着业务的增长和时间的变化&#xff0c;ES 数据库的存储空间越来越大&#xff0c;存储数据多数为系统监控日志&#xff0c;保存的数据不需要长期保留&#xff0c;多数情况只需要保留几个月ES数据即可&#xff0c;既可以减轻ES服务器的负载和资源使用率&#xff0c;还…

Mysql进阶之索引优化

Mysql进阶之索引优化 一. 索引介绍 1.1 什么是Mysql索引 MySQL官方对于索引的定义&#xff1a;索引是帮助MySQL高效获取数据的数据结构。MySQL在存储数据之外&#xff0c;数据库系统中还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种引用(指向)表中的数据…

考研C语言第四章

4.1 关系表达式与逻辑表达式 ps&#xff1a; 算术运算符&#xff1a;加减乘除等 关系运算符&#xff1a;比大小的 逻辑与逻辑或 非&#xff01;的运算级别&#xff08;应该&#xff09;最高 4.2 if-else #include <stdio.h> //上课这个写while的原因是方便一次一次…

怎么导入别人的android项目

到期末了好多同学都问我怎么把别人的安卓项目导进自己电脑里面&#xff0c;今天我来统一解答一下&#xff0c;希望有所帮助。 1.删除项目中原有的自动构建的文件 去到要导入项目的目录下把 .idea .gradle与build 三个文件夹&#xff0c;*.iml&#xff0c;local.properties删除…

JavaScript数组和函数

1. 数组 1.1 init <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice…

肉豆蔻酰五肽-8/Myristoyl Pentapeptide-8/sympeptide222

作用机理----肉豆蔻酰五肽-8 随着年龄增大&#xff0c;皮肤逐渐丧失弹性&#xff0c;肌肉功能弱化&#xff0c;而使眼睛周围皮肤松弛形成眼袋。眼睑部位水分的过度积累即眼睑水肿。肉豆蔻酰五肽-8抑制血管紧张素转换酶&#xff0c;增强眼部淋巴循环&#xff0c;促进水分排出 …

vue面试题汇总

vue面试题汇总 1. 谈谈你对MVVM开发模式的理解&#xff1f;2. v-if 和 v-show 有什么区别&#xff1f;3. r o u t e 和 route和 route和router区别4.vue自定义指令5.vue项目优化6.vue模板如何编译7.vue2响应式原理8.vue3响应式原理9.刷新浏览器后&#xff0c;Vuex的数据是否存在…

Go语言的学习【3】常量

目录 注意事项常量常量还可以用作枚举&#xff1a;iota 报错及解决经验和教训 注意事项 如果在相同的代码块中&#xff0c;我们不可以再次对于相同名称的变量使用初始化声明&#xff0c;例如&#xff1a;a : 20 就是不被允许的&#xff0c;编译器会提示错误 no new variables …

【Linux】死锁

文章目录 死锁关于阻塞的理解死锁的四个必要条件避免死锁的方法 死锁 死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所占用不会释放的资源而处于的一种永久等待状态 单执行流可能导致死锁问题吗? 可能&#xff01;例如:某一个执行流连续申请了…

类型转换(2)

类型转换 知识回顾static_castconst_castreinterpret_cast 类型转换dynamic_cast动态转换和静态转换区别动态转换的使用 知识回顾 static_cast 静态转换应用范围&#xff1a; 基本数据类型的转换&#xff0c;但不能实现基本数据类型指针的转化&#xff0c;但是可以将无类型转…