Spring MVC详解(上)

简介: 本文介绍了MVC架构及其在Spring MVC框架中的应用。MVC将应用程序分为模型、视图和控制器三部分,分别负责数据处理、用户界面展示和请求处理。Spring MVC作为MVC模式的具体实现,不仅是一个web框架,还提供了强大的功能支持,如URL路由映射、参数传递、JSON数据处理、文件上传等。文章详细讲解了如何在Spring MVC中使用`@RequestMapping`、`@RequestParam`、`@RequestBody`、`@PathVariable`等注解来处理HTTP请求,并展示了具体的代码示例和操作步骤。

1. MVC

MVC 是 Model View Controller 的缩写,它是软件⼯程中的⼀种软件架构设计模式,它把软件系统分为模型、视图和控制器三个基本部分。

View (视图): 指在应⽤程序中专⻔⽤来与浏览器进⾏交互,展⽰数据的资源.
Model (模型): 是应⽤程序的主体部分,⽤来处理程序中数据逻辑的部分.
Controller(控制器):可以理解为⼀个分发器,⽤来决定对于视图发来的请求,需要⽤哪⼀个模型来处理,以及处理完后需要跳回到哪⼀个视图。即⽤来连接视图和模型。

2. Spring MVC

Spring MVC 是对 MVC 思想的具体实现,此外 Spring MVC 还是一个 web 框架,所以说 Spring MVC 是一个实现了 MVC 模式的 web 框架

3. 项目创建

Spring MVC 项目的创建和上次的 Spring Boot 项目的步骤一样

3.1. 建立连接

在 Spring MVC 中使用 @RequestMapping 来实现 URL 路由的映射,也就是浏览器链接程序的作用

@RequestMapping 是用来注册接口的路由映射的,表示服务器收到请求时,映射的“/hello”路径就会调用 hello 的方法,路径的名称也可以随便写,不用和方法名保持一致

@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String hello(){
        return "hello spring";
    }
}

运行程序之后,然后在浏览器中输入 http://127.0.0.1:8080/hello 就能访问了

@RestController标识了这是一个控制器类,一个项目中会有很多类和方法,Spring 会对所有的类进行扫描,如果添加了 @RestController注解,才会去访问这个类中有没有 @RequestMapping 注解,如果把 @RestController去掉再访问就会访问不到这个路径

@RequestMapping既可以修饰类,也可以修饰方法,当修饰类和方法时,访问的路径是类路径 + 方法路径,如果不加类路径还是会找不到页面

@RequestMapping 标识⼀个类:设置映射请求的请求路径的初始信息。
@RequestMapping 标识⼀个⽅法:设置映射请求请求路径的具体信息。

@RequestMapping("/user")
@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String hello(){
        return "hello spring";
    }
}

3.2. 请求

@RequestMapping同时支持 post 请求和 get 请求


@RequestMapping("/v1")
public String method(){
    return "v1";
}

使用 postman 发送 get 请求和 post 请求都可以得到响应

可以通过设置来只支持 get 请求或者使用 @GetMapping来限制只支持 get 请求

@RequestMapping(value = "/v2",method = RequestMethod.GET)
public String method1(){
    return "v2";
}
@GetMapping("/v3")
public String method2(){
    return "v3";
}

无论是 v2 还是 v3 都已经不支持 post 请求了

同理,如果设置为只支持 post 请求或者使用 @PostMapping就不会支持 get 请求了

@RequestMapping(value = "/v4",method = RequestMethod.POST)
public String method3(){
    return "v4";
}
@PostMapping("v5")
public String method4(){
    return "v5";
}

在上面的方式中,无论是通过设置 @RequestMapping的方式还是直接使用 @PostMapping@GetMapping 注解的方式都可以

4. 传递参数

4.1. 传递单个参数

接收单个参数,直接在方法中加入参数即可

@RequestMapping("/param")
@RestController
public class ParamController {
    @RequestMapping("/m1")
    public String method1(String name){
        return "接收到参数name: " + name;
    }
}

在传递参数的时候,代码中的参数名称需要和请求的参数名称是一致的

如果传入的参数是整形呢,整数的话是可以用 Integer 和 int 来表示的,下面来演示一下

@RequestMapping("/m2")
public String method2(Integer age){
return "接收到参数age: " + age;
}
@RequestMapping("/m3")
public String method3(int age){
return "接收到参数age: " + age;
}

那么他们的区别是什么呢?

接下来试着不传参,发现 int 定义的参数是直接报错了,Integer 定义的是可以接受 null 的

并且无论是 int 还是 Integer,传入的参数一定要对应,否则也会报错

错误日志上描述的是 String 类型转化为其他类型失败,传递的普通参数,默认的类型是 String ,后端接收时根据定义的类型再进行相应的转化

int 等一些基本数据类型不能传入 null,不过 boolean 是可以传入 null 的


@RequestMapping("/m4")
public String method4(Boolean gender){
return "接收到参数gender: " + gender;
}
@RequestMapping("/m5")
public String method5(boolean gender){
return "接收到参数gender: " + gender;
}

可以看出,如果不传参数的话,Boolean 接收到的是 null,boolean 接收到的 false,boolean 类型默认就是 false

4.2. 传递多个参数

多个参数也是可以直接传递的,并且不需要保证传参的顺序,只需要参数名对应即可

@RequestMapping("/m6")
public String method6(String name,Integer age){
    return "接收到参数name: " + name + "," + "age: " + age ;
}

再传递几个参数也是可以的,不过这种方式传递这么多参数就有些麻烦了,可以通过传递对象的方式可以传入多个参数

创建一个 User 的类(重写了 toString() 方法)之后再进行对象的传递

@RequestMapping("/m7")
public String method7(User user){
return "接收到参数user: " + user;
}

通过传入对象的方式就可以进行多个参数的传递,在之前,如果 int 类型参数没有传的话是会报错的,如果对象中使用 int 类型来描述属性的话,传递参数时不传也不会报错:

因为 age 是成员变量,是有默认值的,即使如此,还是建议使用包装类型,可以避免一些难以预料的问题

4.3. 参数重命名

在上面提到过,前端传递的参数要和后端方法里的参数保持一致,而前端可能会传入各种各样的参数,比如 userName, productName 等,后端如果只想要一个 name 的话可以对前端传递的参数进行重命名,把前端传入的名称都重命名为 name,后续就使用 name 进行操作,这就需要使用到 @RequestParam注解

@RequestMapping("/m8")
public String method8(@RequestParam("userName") String name){
    return "接收到参数name: " + name;
}

@RequestParam就起到了参数绑定的作用,把前端的 userName 和后端的 name 进行了绑定,那如果前端还是传入 name 会怎么样呢

直接报了一个 400 的错误,并且无论是传入参数和名称不对应还是不传递参数,都会报错

意思就是传递 userName 的参数,相当于强制绑定

来看 @RequestParam的源码:

value(或name)属性:用于指定请求参数的名称

required属性:表示该参数是否必须在请求中存在。如果设置为true,当请求中没有这个参数时,会抛出异常。如果设置为false,则在请求中没有该参数时,会使用默认值(如果有设置默认值)或者为null

在使用@RequestParam注解时,如果只提供一个字符串参数,它会被视为value(或name)属性的值。

@RequestMapping("/m8")
public String method8(@RequestParam(value = "userName",required = false) String name){
    return "接收到参数name: " + name;
}

把 required 改为 false 不传递参数也不会报错,会使用默认值或者 null

4.4. 传递数组

@RequestMapping("/m9")
public String method9(String[] arr){
    return "接收到参数name: " + List.of(arr);
}

传递数组的方式是有两种的,第一种就是直接传入数组中的数据,第二种就是传入多个数据,参数一致

那么第一种传入的数据是按照字符来传的还是按照数组中的三个元素来传的

调试一下发现是按照数组元素的形式自动切割了

4.5. 传递集合

数组可以通过上面的方式传,那么 List 行不行呢

@RequestMapping("/m10")
public String method10(List<String> list){
    return "接收到参数list: " + list;
}

这一次是直接报错了,显示 500 的状态码

抛出了异常,无法构造一个 List

原因是发送上述请求时,默认是把请求的参数封装成数组的,并不是一个 List,所以需要进行参数绑定,也就是把数组转化为 List

@RequestMapping("/m10")
public String method10(@RequestParam List<String> list){
    return "接收到参数list: " + list;
}

4.6. 传递 JSON 数据

4.6.1. JSON 语法

  1. 数据在键值对(Key/Value)中;
  2. 数据由逗号 , 分隔;
  3. 对象用 {} 表示;
  4. 数组用 [] 表示;
  5. 值可以为对象,也可以为数组,数组中可以包含多个对象。

接下来看 JSON 是怎么转化为 java 对象的:

使用 ObjectMapper 对象提供的两个⽅法,可以完成对象和 JSON 字符串的互转。
writeValueAsString:把对象转为 JSON 字符串。
readValue:把字符串转为对象。

public class JsonTest {
    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        User user = new User();
        user.setName("zhangsan");
        user.setGender(1);
        user.setAge(18);
        //对象转JSON
        String s = objectMapper.writeValueAsString(user);
        System.out.println(s);
        //JSON转对象
        User user1 = objectMapper.readValue(s,User.class);
        System.out.println(user1);
    }
}

4.6.2. 传递 JSON

传递 JSON 参数是通过@RequestBody注解来实现的,从请求正文中获取数据

@RequestMapping("/m11")
public String method11(@RequestBody User user){
    return "接收到参数user: " + user;
}

可以看出,请求的数据格式是不同的

4.7. 获取 URL 中的参数

获取 URL 中的参数是通过 @PathVariable 注解实现的

@RequestMapping("/article/{articleId}")
public String method12(@PathVariable("articleId") String articleId){
    return "接收到参数articleId: " + articleId;
}

@RequestMapping("/article/{articleId}")大括号中的内容就相当于占位符,可以传入任何参数,但是不能不传

多个参数也是可以接受的:

@RequestMapping("/article/{articleId}/{name}")
public String method12(@PathVariable String articleId,@PathVariable String name){
    return "接收到参数articleId: " + articleId + ", name :" + name;
}

前面也提到过,路径中的参数相当于占位符,不能少传,顺序也必须一致

把 required 改为 false 也不行

4.8. 上传文件

通过 MultipartFile可以获取文件的对象

@RequestMapping("/m12")
public String method13(MultipartFile file){
System.out.println(file.getOriginalFilename());
return "接收到参数file: " + file.getOriginalFilename();
}

通过 Fiddler 抓包可以看到上传的二进制文件

这里的重命名和上面的是不一样的,这里使用的是 @RequestPart注解

相关文章
|
关系型数据库 MySQL 数据挖掘
MYSQL日期与时间函数的实用技巧
MYSQL日期函数与时间函数是数据库操作的关键工具,可轻松处理、查询、比较和格式化日期时间数据。它们能提取日期的年、月、日等部分,便于筛选和统计;同时,也能处理时间数据,如计算时间差、获取当前时间,助力用户更好地管理时间信息。掌握这些函数,不仅能提升数据库操作效率,还能为数据分析和报表生成提供有力支持。无论初学者还是资深数据库管理员,精通MYSQL的日期和时间函数都至关重要,以满足各种数据处理需求,确保数据的准确性和高效性。
804 0
|
算法 定位技术
八爪鱼RPA在微信的十大高频场景,让你的工作事半功倍!
在微信中,rpa(机器人流程自动化)技术可以应用于各种情况,为用户提供更高效、便捷的工作体验。本文将介绍微信中的十大高频场景,并说明rpa可以如何应用于这些场景中,从而让工作事半功倍。
|
移动开发 前端开发 Swift
iOS 最好的应用程序开发编程语言竟然是这7种
iOS 最好的应用程序开发编程语言竟然是这7种
468 8
|
弹性计算 人工智能 安全
阿里云弹性计算:助力企业实现灵活扩展与高效计算
【10月更文挑战第6天】在现代企业的数字化转型中,云计算已经成为不可或缺的技术基础。阿里云弹性计算(Elastic Compute Service, ECS)凭借其强大的弹性伸缩能力、高可用性和灵活性,帮助企业在云端实现高效的业务运营和资源管理。本文将探讨阿里云弹性计算的主要功能、技术优势以及在各行业中的应用场景。
658 7
|
12月前
「Mac畅玩鸿蒙与硬件46」UI互动应用篇23 - 自定义天气预报组件
本篇将带你实现一个自定义天气预报组件。用户可以通过选择不同城市来获取相应的天气信息,页面会显示当前城市的天气图标、温度及天气描述。这一功能适合用于动态展示天气信息的小型应用。
532 38
「Mac畅玩鸿蒙与硬件46」UI互动应用篇23 - 自定义天气预报组件
|
12月前
|
算法 前端开发 API
开源轻量级IM框架MobileIMSDK的鸿蒙NEXT客户端库已发布
MobileIMSDK-鸿蒙端是一套基于鸿蒙Next(纯血鸿蒙)系统的IM即时通讯客户端库: 1)超轻量级(编译后库文件仅50KB)、无任何第3方库依赖(开箱即用); 2)纯ArkTS编写、无Native代码、高度提炼、简单易用; 3)基于鸿蒙Next标准WebSocket API,简洁优雅; 4)可运行于任何支持鸿蒙Next的平台; 5)能与 MobileIMSDK的各种客户端完美互通; 6)可应用于鸿蒙Next中的消息推送、客服聊天、企业OA、IM等场景。
374 45
|
监控 数据可视化 数据挖掘
ERP系统中的数据分析与决策支持解析
【7月更文挑战第25天】 ERP系统中的数据分析与决策支持解析
1145 0
|
存储 Python
数据包络分析(Data Envelopment Analysis, DEA)详解与Python代码示例
数据包络分析(Data Envelopment Analysis, DEA)详解与Python代码示例
|
缓存 JSON 运维
深入探讨API调用性能优化与错误处理
随着互联网技术的不断发展,API(应用程序接口)已经成为软件系统中重要的组成部分。而优化API调用的性能以及处理错误和异常情况则是保障系统稳定性和可靠性的关键。本文将从以下几个方面来探讨如何进行性能优化和错误处理。
|
消息中间件 微服务
微服务通信:RPC、消息队列和事件驱动架构的比较
在微服务架构中,微服务之间的通信是至关重要的。为了实现松耦合、高效可靠的通信,开发人员可以选择不同的通信方式,包括RPC(远程过程调用)、消息队列和事件驱动架构。本文将对这三种常见的微服务通信方式进行比较,探讨它们的特点、适用场景和优缺点,帮助开发人员选择合适的通信方式。
706 0