开发必备技能:探索Spring MVC请求映射和参数绑定的奇妙之旅!

简介: 开发必备技能:探索Spring MVC请求映射和参数绑定的奇妙之旅!


1.1 设置请求映射路径

1.1.1 环境准备

@Controller
public class UserController {
    @RequestMapping("/save")
    @ResponseBody
    public String save(){
        System.out.println("user save ...");
        return "{'module':'user save'}";
    }
    @RequestMapping("/delete")
    @ResponseBody
    public String save(){
        System.out.println("user delete ...");
        return "{'module':'user delete'}";
    }
}
@Controller
public class BookController {
    @RequestMapping("/save")
    @ResponseBody
    public String save(){
        System.out.println("book save ...");
        return "{'module':'book save'}";
    }
}

把环境准备好后,启动Tomcat服务器,后台会报错:

从错误信息可以看出:

  • UserController有一个save方法,访问路径为http://localhost/save
  • BookController也有一个save方法,访问路径为http://localhost/save
  • 当访问http://localhost/saved的时候,到底是访问UserController还是BookController?

1.1.2 问题分析

团队多人开发,每人设置不同的请求路径,冲突问题该如何解决?

解决思路:为不同模块设置模块名作为请求路径前置。

对于Book模块的save,将其访问路径设置http://localhost/book/save

对于User模块的save,将其访问路径设置http://localhost/user/save

这样在同一个模块中出现命名冲突的情况就比较少了。

1.1.3 设置映射路径

步骤1:修改Controller
@Controller
public class UserController {
    @RequestMapping("/user/save")
    @ResponseBody
    public String save(){
        System.out.println("user save ...");
        return "{'module':'user save'}";
    }
}
@Controller
public class BookController {
    @RequestMapping("/book/save")
    @ResponseBody
    public String save(){
        System.out.println("book save ...");
        return "{'module':'book save'}";
    }
}

问题是解决了,但是每个方法前面都需要进行修改,写起来比较麻烦而且还有很多重复代码,如果/user后期发生变化,所有的方法都需要改,耦合度太高。

步骤2:优化路径配置

优化方案:

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/save")
    @ResponseBody
    public String save(){
        System.out.println("user save ...");
        return "{'module':'user save'}";
    }
    ............
}
@Controller
@RequestMapping("/book")
public class BookController {
    @RequestMapping("/save")
    @ResponseBody
    public String save(){
        System.out.println("book save ...");
        return "{'module':'book save'}";
    }
}

注意:

  • 当类上和方法上都添加了@RequestMapping注解,前端发送请求的时候,要和两个注解的value值相加匹配才能访问到。
  • @RequestMapping注解value属性前面加不加/都可以。

1.2 请求参数

请求路径设置好后,只要确保页面发送请求地址和后台Controller类中配置的路径一致,就可以接收到前端的请求,接收到请求后,如何接收页面传递的参数?

关于请求参数的传递与接收是和请求方式有关系的,目前比较常见的两种请求方式为:

  • GET
  • POST

针对于不同的请求前端如何发送,后端如何接收?

1.2.1 参数传递

GET发送单个参数

发送请求与参数:

http://localhost/commonParam?name=itcast

接收参数:

@Controller
public class UserController {
    @RequestMapping("/commonParam")
    @ResponseBody
    public String commonParam(String name){
        System.out.println("普通参数传递 name ==> "+name);
        return "{'module':'commonParam'}";
    }
}
GET发送多个参数

发送请求与参数:

http://localhost/commonParam?name=itcast&age=15

接收参数:

@Controller
public class UserController {
    @RequestMapping("/commonParam")
    @ResponseBody
    public String commonParam(String name,int age){
        System.out.println("普通参数传递 name ==> "+name);
        System.out.println("普通参数传递 age ==> "+age);
        return "{'module':'commonParam'}";
    }
}
GET请求中文乱码

如果我们传递的参数中有中文,你会发现接收到的参数会出现中文乱码问题。

发送请求:http://localhost/commonParam?name=张三&age=18

控制台:

出现乱码的原因相信大家都清楚,Tomcat8.5以后的版本已经处理了中文乱码的问题,但是IDEA中的Tomcat插件目前只到Tomcat7,所以需要修改pom.xml来解决GET请求中文乱码问题。

<build>
    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.1</version>
        <configuration>
          <port>80</port><!--tomcat端口号-->
          <path>/</path> <!--虚拟目录-->
          <uriEncoding>UTF-8</uriEncoding><!--访问路径编解码字符集-->
        </configuration>
      </plugin>
    </plugins>
  </build>
POST发送参数

发送请求与参数:

接收参数:

和GET一致,不用做任何修改。

@Controller
public class UserController {
    @RequestMapping("/commonParam")
    @ResponseBody
    public String commonParam(String name,int age){
        System.out.println("普通参数传递 name ==> "+name);
        System.out.println("普通参数传递 age ==> "+age);
        return "{'module':'commonParam'}";
    }
}
POST请求中文乱码

发送请求与参数:

接收参数:

控制台打印,会发现有中文乱码问题。

解决方案:配置过滤器

public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
    //乱码处理
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        return new Filter[]{filter};
    }
}

CharacterEncodingFilter是在spring-web包中,所以用之前需要导入对应的jar包。

1.3 五种类型参数传递

前面我们已经能够使用GET或POST来发送请求和数据,所携带的数据都是比较简单的数据,接下来在这个基础上,我们来研究一些比较复杂的参数传递,常见的参数种类有:

  • 普通参数
  • POJO类型参数
  • 嵌套POJO类型参数
  • 数组类型参数
  • 集合类型参数

1.3.1 普通参数

  • 普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数。

如果形参与地址参数名不一致该如何解决?

发送请求与参数:

http://localhost/commonParamDifferentName?name=张三&age=18

后台接收参数:

@RequestMapping("/commonParamDifferentName")
@ResponseBody
public String commonParamDifferentName(String userName , int age){
    System.out.println("普通参数传递 userName ==> "+userName);
    System.out.println("普通参数传递 age ==> "+age);
    return "{'module':'common param different name'}";
}

因为前端给的是name,后台接收使用的是userName,两个名称对不上,导致接收数据失败:

解决方案:使用@RequestParam注解

@RequestMapping("/commonParamDifferentName")
    @ResponseBody
    public String commonParamDifferentName(@RequestPaam("name") String userName , int age){
        System.out.println("普通参数传递 userName ==> "+userName);
        System.out.println("普通参数传递 age ==> "+age);
        return "{'module':'common param different name'}";
    }

注意:写上@RequestParam注解框架就不需要自己去解析注入,能提升框架处理性能

1.3.2 POJO数据类型

简单数据类型一般处理的是参数个数比较少的请求,如果参数比较多,那么后台接收参数的时候就比较复杂,这个时候我们可以考虑使用POJO数据类型。

  • POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数

此时需要使用前面准备好的POJO类,先来看下User

public class User {
    private String name;
    private int age;
    //setter...getter...略
}

发送请求和参数:

后台接收参数:

//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
    System.out.println("pojo参数传递 user ==> "+user);
    return "{'module':'pojo param'}";
}

注意:

  • POJO参数接收,前端GET和POST发送请求数据的方式不变。
  • 请求参数key的名称要和POJO中属性的名称一致,否则无法封装。

1.3.3 嵌套POJO类型参数

如果POJO对象中嵌套了其他的POJO类,如

public class Address {
    private String province;
    private String city;
    //setter...getter...略
}
public class User {
    private String name;
    private int age;
    private Address address;
    //setter...getter...略
}
  • 嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数

发送请求和参数:

后台接收参数:

//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
    System.out.println("pojo参数传递 user ==> "+user);
    return "{'module':'pojo param'}";
}

注意:

请求参数key的名称要和POJO中属性的名称一致,否则无法封装

1.3.4 数组类型参数

举个简单的例子,如果前端需要获取用户的爱好,爱好绝大多数情况下都是多个,如何发送请求数据和接收数据呢?

  • 数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数

发送请求和参数:

后台接收参数:

//数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
    @RequestMapping("/arrayParam")
    @ResponseBody
    public String arrayParam(String[] likes){
        System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));
        return "{'module':'array param'}";
    }

1.3.5 集合类型参数

数组能接收多个值,那么集合是否也可以实现这个功能呢?

发送请求和参数:

后台接收参数:

//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(List<String> likes){
    System.out.println("集合参数传递 likes ==> "+ likes);
    return "{'module':'list param'}";
}

运行会报错,

错误的原因是:SpringMVC将List看做是一个POJO对象来处理,将其创建一个对象并准备把前端的数据封装到对象中,但是List是一个接口无法创建对象,所以报错。

解决方案是:使用@RequestParam注解

//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
    System.out.println("集合参数传递 likes ==> "+ likes);
    return "{'module':'list param'}";
}
  • 集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系
  • 对于简单数据类型使用数组会比集合更简单些。

知识点1:@RequestParam

名称 @RequestParam
类型 形参注解
位置 SpringMVC控制器方法形参定义前面
作用 绑定请求参数与处理器方法形参间的关系
相关参数 required:是否为必传参数
defaultValue:参数默认值

1.4 JSON数据传输参数

现在比较流行的开发方式为异步调用。前后台以异步方式进行交换,传输的数据使用的是JSON,所以前端如果发送的是JSON数据,后端该如何接收?

对于JSON数据类型,我们常见的有三种:

  • json普通数组([“value1”,“value2”,“value3”,…])
  • json对象({key1:value1,key2:value2,…})
  • json对象数组([{key1:value1,…},{key2:value2,…}])

对于上述数据,前端如何发送,后端如何接收?

JSON普通数组

步骤1:pom.xml添加依赖

SpringMVC默认使用的是jackson来处理json的转换,所以需要在pom.xml添加jackson依赖

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.0</version>
</dependency>
步骤2:PostMan发送JSON数据

步骤3:开启SpringMVC注解支持

在SpringMVC的配置类中开启SpringMVC的注解支持,这里面就包含了将JSON转换成对象的功能。

@Configuration
@ComponentScan("com.itheima.controller")
//开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}
步骤4:参数前添加@RequestBody
//使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes){
    System.out.println("list common(json)参数传递 list ==> "+likes);
    return "{'module':'list common for json param'}";
}
步骤5:启动运行程序

JSON普通数组的数据就已经传递完成,下面针对JSON对象数据和JSON对象数组的数据该如何传递呢?

JSON对象数据

我们会发现,只需要关注请求和数据如何发送?后端数据如何接收?

请求和数据的发送:

{
  "name":"itcast",
  "age":15
}

后端接收数据:

@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user){
    System.out.println("pojo(json)参数传递 user ==> "+user);
    return "{'module':'pojo for json param'}";
}

启动程序访问测试

说明:

address为null的原因是前端没有传递数据给后端。

如果想要address也有数据,我们需求修改前端传递的数据内容:

{
  "name":"itcast",
  "age":15,
    "address":{
        "province":"beijing",
        "city":"beijing"
    }
}

再次发送请求,就能看到address中的数据

JSON对象数组

集合中保存多个POJO该如何实现?

请求和数据的发送:

[
    {"name":"itcast","age":15},
    {"name":"itheima","age":12}
]

后端接收数据:

@RequestMapping("/listPojoParamForJson")
@ResponseBody
public String listPojoParamForJson(@RequestBody List<User> list){
    System.out.println("list pojo(json)参数传递 list ==> "+list);
    return "{'module':'list pojo for json param'}";
}

启动程序访问测试

小结

SpringMVC接收JSON数据的实现步骤为:

(1)导入jackson包

(2)使用PostMan发送JSON数据

(3)开启SpringMVC注解驱动,在配置类上添加@EnableWebMvc注解

(4)Controller方法的参数前添加@RequestBody注解

知识点1:@EnableWebMvc

名称 @EnableWebMvc
类型 配置类注解
位置 SpringMVC配置类定义上方
作用 开启SpringMVC多项辅助功能

知识点2:@RequestBody

名称 @RequestBody
类型 形参注解
位置 SpringMVC控制器方法形参定义前面
作用 将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次

@RequestBody与@RequestParam区别

  • 区别
  • @RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】
  • @RequestBody用于接收json数据【application/json】
  • 应用
  • 后期开发中,发送json格式数据为主,@RequestBody应用较广
  • 如果发送非json格式数据,选用@RequestParam接收请求参数

1.5 日期类型参数传递

前面我们处理过简单数据类型、POJO数据类型、数组和集合数据类型以及JSON数据类型,接下来我们还得处理一种开发中比较常见的一种数据类型,日期类型

日期类型比较特殊,因为对于日期的格式有N多中输入方式,比如:

  • 2088-08-18
  • 2088/08/18
  • 08/18/2088

针对这么多日期格式,SpringMVC该如何接收,它能很好的处理日期类型数据么?

步骤1:编写方法接收日期数据

在UserController类中添加方法,把参数设置为日期类型

@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date)
    System.out.println("参数传递 date ==> "+date);
    return "{'module':'data param'}";
}
步骤2:启动Tomcat服务器

查看控制台是否报错,如果有错误,先解决错误。

步骤3:使用PostMan发送请求

使用PostMan发送GET请求,并设置date参数

http://localhost/dataParam?date=2088/08/08

步骤4:查看控制台

通过打印,我们发现SpringMVC可以接收日期数据类型,并将其打印在控制台。

这个时候,我们就想如果把日期参数的格式改成其他的,SpringMVC还能处理么?

步骤5:更换日期格式

为了能更好的看到程序运行的结果,我们在方法中多添加一个日期参数。

@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date,Date date1)
    System.out.println("参数传递 date ==> "+date);
    return "{'module':'data param'}";
}

使用PostMan发送请求,携带两个不同的日期格式,

http://localhost/dataParam?date=2088/08/08&date1=2088-08-08

发送请求和数据后,页面会报400,控制台会报出一个错误

Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type ‘java.lang.String’ to required type ‘java.util.Date’; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Date] for value ‘2088-08-08’; nested exception is java.lang.IllegalArgumentException]

从错误信息可以看出,错误的原因是在将2088-08-08转换成日期类型的时候失败了,原因是SpringMVC默认支持的字符串转日期的格式为yyyy/MM/dd,而我们现在传递的不符合其默认格式,SpringMVC就无法进行格式转换,所以报错。

解决方案也比较简单,需要使用@DateTimeFormat

@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date,
                        @DateTimeFormat(pattern="yyyy-MM-dd") Date date1)
    System.out.println("参数传递 date ==> "+date);
  System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1);
    return "{'module':'data param'}";
}

重新启动服务器,重新发送请求测试,SpringMVC就可以正确的进行日期转换了

步骤6:携带时间的日期

接下来我们再来发送一个携带时间的日期,看下SpringMVC该如何处理?

先修改UserController类,添加第三个参数

@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date,
                        @DateTimeFormat(pattern="yyyy-MM-dd") Date date1,
                        @DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2)
    System.out.println("参数传递 date ==> "+date);
  System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1);
  System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2);
    return "{'module':'data param'}";
}

使用PostMan发送请求,携带两个不同的日期格式,

http://localhost/dataParam?date=2088/08/08&date1=2088-08-08&date2=2088/08/08 8:08:08

重新启动服务器,重新发送请求测试,SpringMVC就可以将日期时间的数据进行转换

知识点1:@DateTimeFormat
名称 @DateTimeFormat
类型 形参注解
位置 SpringMVC控制器方法形参前面
作用 设定日期时间型数据格式
相关属性 pattern:指定日期时间格式字符串

内部实现原理

我们需要先思考个问题:

  • 前端传递字符串,后端使用日期Date接收。
  • 前端传递JSON数据,后端使用对象接收。
  • 前端传递字符串,后端使用Integer接收。
  • 后台需要的数据类型有很多中
  • 在数据的传递过程中存在很多类型的转换

问:谁来做这个类型转换?

答:SpringMVC

问:SpringMVC是如何实现类型转换的?

答:SpringMVC中提供了很多类型转换接口和实现类

在框架中,有一些类型转换接口,其中有:

  • (1) Converter接口
/**
* S: the source type
* T: the target type
*/
public interface Converter<S, T> {
    @Nullable
    //该方法就是将从页面上接收的数据(S)转换成我们想要的数据类型(T)返回
    T convert(S source);
}

注意:Converter所属的包为org.springframework.core.convert.converter

Converter接口的实现类

框架中有提供很多对应Converter接口的实现类,用来实现不同数据类型之间的转换,如:

请求参数年龄数据(String→Integer)

日期格式转换(String → Date)

  • (2) HttpMessageConverter接口

该接口是实现对象与JSON之间的转换工作

注意:SpringMVC的配置类把@EnableWebMvc当做标配配置上去,不要省略

后记
👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹

相关文章
|
12天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
562 7
|
23天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
23天前
|
Java 数据库连接 Maven
Spring基础1——Spring(配置开发版),IOC和DI
spring介绍、入门案例、控制反转IOC、IOC容器、Bean、依赖注入DI
Spring基础1——Spring(配置开发版),IOC和DI
|
1月前
|
缓存 Java 应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
58 2
|
1月前
|
NoSQL 前端开发 Java
使用 Spring Boot + Neo4j 实现知识图谱功能开发
在数据驱动的时代,知识图谱作为一种强大的信息组织方式,正逐渐在各个领域展现出其独特的价值。本文将围绕使用Spring Boot结合Neo4j图数据库来实现知识图谱功能开发的技术细节进行分享,帮助读者理解并掌握这一技术栈在实际项目中的应用。
102 4
|
1月前
|
安全 Java 开发者
强大!Spring Cloud Gateway新特性及高级开发技巧
在微服务架构日益盛行的今天,网关作为微服务架构中的关键组件,承担着路由、安全、监控、限流等多重职责。Spring Cloud Gateway作为新一代的微服务网关,凭借其基于Spring Framework 5、Project Reactor和Spring Boot 2.0的强大技术栈,正逐步成为业界的主流选择。本文将深入探讨Spring Cloud Gateway的新特性及高级开发技巧,助力开发者更好地掌握这一强大的网关工具。
112 6
|
1月前
|
IDE Java 开发工具
还在为繁琐的配置头疼吗?一文教你如何用 Spring Boot 快速启动,让开发效率飙升,从此告别加班——打造你的首个轻量级应用!
【9月更文挑战第2天】Spring Boot 是一款基于 Spring 框架的简化开发工具包,采用“约定优于配置”的原则,帮助开发者快速创建独立的生产级应用程序。本文将指导您完成首个 Spring Boot 项目的搭建过程,包括环境配置、项目初始化、添加依赖、编写控制器及运行应用。首先需确保 JDK 版本不低于 8,并安装支持 Spring Boot 的现代 IDE,如 IntelliJ IDEA 或 Eclipse。
87 5
|
5月前
|
设计模式 前端开发 JavaScript
Spring MVC(一)【什么是Spring MVC】
Spring MVC(一)【什么是Spring MVC】
|
4月前
|
设计模式 前端开发 Java
【Spring MVC】快速学习使用Spring MVC的注解及三层架构
【Spring MVC】快速学习使用Spring MVC的注解及三层架构
49 1
|
4月前
|
前端开发 Java 应用服务中间件
Spring框架第六章(SpringMVC概括及基于JDK21与Tomcat10创建SpringMVC程序)
Spring框架第六章(SpringMVC概括及基于JDK21与Tomcat10创建SpringMVC程序)
下一篇
无影云桌面