目录
前言
什么是Spring Web MVC?
什么是MVC
什么是Spring MVC
Spring MVC和Spring Boot的区别
什么是Spring Boot
关系和区别
学习Spring MVC
注解学习
@SpringBootApplication
@RestController
@RequestMapping
@RequestMapping的使用
@RequestMapping能够接受的是GET请求还是POST请求?
请求
传递单个参数
后端参数重命名
@RequestParam
非必传参数设置
传递多个参数
传递对象
传递数组
编辑 传递集合
传递JSON格式数据
JSON语法格式
JSON的两种结构
获取URL中参数@PathVariable
上传⽂件@RequestPart
前言
在前面,我们了解了什么是Spring,那么本篇我们就来认识一种在网站开发中常用的框架———Spring Web MVC。
什么是Spring Web MVC?
我们来看下官方的解释:
翻译过来就是:
Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就包含在Spring框架中。它的正式名称为”Spring Web MVC”来自于其源模块的名称(Spring-webmvc),但通常被称为“Spring MVC”。
简单来说:Spring Web MVC就是一个基于Java实现了Web MVC设计模式的框架,采用了MVC架构模式的思想,将应用程序的不同方面(输入逻辑、业务逻辑和 UI 逻辑)分离开来,降低了代码的耦合度。
什么是MVC
MVC是Model View Controller的缩写,是软件工程中的一种软件架构设计模式,将软件系统分为模型(model)、视图(view)和控制器(controller)三个部分。
- 模型(Model):负责处理业务逻辑和数据存储,是应用程序的主体部分;
- 视图(View):负责展示数据界面,专门用来与用户、浏览器进行交互;
- 控制器(Controller):可以理解为一个分发器,用来决定视图发来的请求,需要用哪个模型来处理,以及处理完要跳回哪一个视图,即用来连接视图和模型。
MVC架构模式通常用于应用程序和Web网站开发中。在Web网站开发中,控制器通常是由Servlet或者Controller类来实现的,模型由java类实现,视图由HTML或者JSP界面来实现。通过MVC模式,能让程序的各个部分更加独立,易于维护和扩展。
什么是Spring MVC
MVC是一种架构设计模式,也是一种思想,而Spring MVC则是对MVC思想的具体实现,此外,SpringMVC还是一个Web框架。
简单来说:Spring MVC就是一个实现了MVC架构模式的Web框架。
在前面创建Spring Boot项目的时候,其实勾选的Spring Web框架就是Spring MVC框架。
这里可能就有人不明白了,怎么是Spring Boot项目又是Spring MVC项目?二者有什么联系?
Spring MVC和Spring Boot的区别
什么是Spring Boot
Spring Boot是基于 Spring 框架的开源 Java 基础框架,由 Pivotal 团队(现为 VMware)开发。它旨在简化 Spring 应用的初始搭建、开发和部署过程,让开发者能够更快速地构建基于 Spring 的应用程序。
关系和区别
Spring Boot和Spring MVC是紧密联系的,但在功能和定位上有所不同。
Spring Boot 是一个基于 Spring 框架的开源框架,通过“约定优于配置”的理念,极大地简化了 Spring 应用的开发和部署过程。它提供了丰富的启动器、自动配置功能和生产级监控管理功能,让开发者能够更快速地构建和部署基于 Spring 的应用程序。
而SpringMVC则是用于构建Web应用程序的模块。
Spring Boot是Spring MVC的一种优化和升级,通过自动配置和内置的工具简化了开发过程,提高了开发效率。同时Spring Boot 也支持使用其他Spring模块,可以集成进去。
Sping Boot只是实现Spring MVC的一种方式而已。
简单来说,Spring MVC是通过Spring Boot添加Spring Web MVC框架依赖来实现web功能的。
Spring MVC 虽然实现了MVC架构模式,但根据自身特点,也做出了一些改变:
学习Spring MVC
Spring MVC既然是Web框架,那么当用户在浏览器输入URL之后,Spring MVC项目就能感知到用户的请求,并做出响应。
这里我们学习Spring MVC,主要也是学习如果通过浏览器和用户程序进行交互。
主要有以下三个方面:
- 建立连接:将用户(浏览器)和java程序连接起来,即访问一个地址能够调用到我们的Spring程序;
- 请求:用户发送请求时会带一些参数,在java程序中要想办法获取到参数,所以请求这块小主要是获取参数的功能;
- 响应:执行了业务逻辑之后,要将程序的执行结果返回给用户,也就是响应。
举个例子:
比如我们去银行存款,那么我们首先就是要去ATM机(建立连接),将我们的银行卡插入卡槽并将钱放入ATM机中(请求),当ATM机帮我们存完钱后,会在界面上提示存款成功(响应)。
学习Spring MVC,那么我们就要学习它的注解,在Spring MVC项目中,会用到非常多的注解。
注解学习
@SpringBootApplication
当我们创建好一个Spring MVC项目后,就会自动生成一个“项目名+Application”的类,并在这个类中我们可以看到有一个注解。
@SpringBootApplication是@SpringBootConfiguration和@EnableAutoConfiguration以及@ComponentScan的组合,这个注解能够让我们快速开始一个Spring Boot项目。
- @SpringBootConfiguration:表示当前类是一个Spring Boot的配置类,包含了一些基础的Spring Boot配置;
- @EnableAutoConfiguration:该注解告诉Spring Boot,根据当前项目中的类路径以及已经配置好的属性,自动地选择和配置需要的Bean,大大简化了新项目的配置。
- @ComponentScan:启动组件扫描功能,自动扫描并注册带有
@Component
、@Service
、@Controller
、@Repository
等注解的类为 Spring Bean。默认情况下,会扫描当前类所在的包以及子包中的组件。
当在SpringBoot项目中使用@SpringBootApplication注解时,它会自动配置一个开发环境,以便可以快速启动并测试程序。
注:由@SpringBootApplication注解的类是整个SpringBoot项目的启动类,项目是从这个类启动的。
那么我们怎么来实现浏览器和java项目之间的交互?
我们先创建完一个SpringMVC项目,过程和创建SpringBoot项目的过程一样。
我们先实现一个简单的代码,再来进行解释:
java">package com.example.demo.Controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class HelloController {@RequestMapping("/sayHi")public String hello(){return "Hello World";}
}
写完上面的代码,我们再开启带有@SpringBootApplication的类,当将SpringMVC项目启动完后,会是下面这样的结果:
这样说明我们的SpringMVC项目启动成功。
我们就可以用我们的浏览器进行访问当前的SpringMVC项目,在浏览器中输入“http://127.0.0.1:8080/sayHi",为什么要输入URL?
Spring是在Servlet的基础上进行开发的,所以Spring使用的服务器默认绑定的是Tomcat服务器,Tomcat默认绑定的是8080端口号,所以我们访问端口号8080即可,当然,我们不想使用这个端口号也是可以的,后续再讲。
当在电脑上启动SpringBoot项目时,通常会使用127.0.0.1或者localhost作为访问地址。SpringBoot项目默认将嵌入式服务器(如Tomcat)绑定到127.0.0.1,这说明应用程序只用从本地计算机访问,我们也可以在配置文件,如在 application.properties
或 application.yml
文件中,可以通过配置 server.address
属性来指定绑定的 IP 地址。如果不指定,默认值为 127.0.0.1
。
此外,“/sayHi”则是我们RequestMapping中的内容。
在上面的代码中,出现了两个新的注解@RestController和@RequestMapping,我们来学习这两个新的注解。
@RestController
在SpringMVC项目中,会有很多类,那么Spring程序怎么知道哪些类的方法要执行呢?
Spring会对所有的类进行扫描,如果类加了注解@RestController,才会去扫描这个类里面的方法有没有加@RequestMapping注解。
@RequestMapping
在Spring MVC项目中使用@RequestMapping来实现URL路由映射,也就是浏览器连接程序的作用,这个注解是在Spring MVC应用程序中最常被用到的注解之一!
路由映射的作用是将客户端发送的 HTTP 请求(包括请求的 URL、HTTP 方法等信息)映射到具体的处理器方法(Controller 方法)。这样,Spring Boot 应用可以根据不同的请求路径和方法,执行不同的业务逻辑。
当我们要去访问Spring MVC程序的时候,在端口号后面的/satHi就表示访问/sayHi里有映射的方法。
在@RequestMapping中的“/”是可以省略的,Spring会自动为我们加上,但为了养成好习惯,推荐还是要加上。
@RequestMapping作为在Spring MVC项目中常用的注解之一,我们来深入学习一下@RequestMapping的使用。
@RequestMapping的使用
@RequestMapping注解不仅可以修饰方法,还可以修饰类,当修饰类和方法时,访问的地址是类路径+方法路径。
@RequestMapping修饰类:表示设置映射请求的请求路径的初始信息;
@RequestMapping修饰方法:表示设置映射请求的请求路径的具体信息。
示例:
java">package com.example.demo.Controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/hello")
@RestController
public class HelloController {@RequestMapping("sayHi")public String hello(){return "Hello World";}
}
我们的访问路径为:http:127.0.0.1:8080/hello/sayHi
那么在@RequestMapping中,我们的URL路径也可以是多层路径,但最终访问,还是类路径+方法路径。
@RequestMapping能够接受的是GET请求还是POST请求?
我们来测试一下就知道。
我们可以借助postman来构建出get请求和post请求。
我们可以看到,通过postman构造出来的GET和POST请求出来,@RequestMapping默认支持GET请求和POST请求,那么我们如果只想要它能够接受GET请求或POST请求,我们应该怎么做?
我们来看下@RequestMapping的源码:
我们来看下在@RequestMapping中的注解:
- @Target({ElementType.TYPE, ElementType.METHOD}):这个注解可以用于类级别或者方法级别;
- @Retention(RetentionPolicy.RUNTIME):这个注解在运行时有效,即运行时的代码可以访问这个注解的信息;
- @Documented:这个一个标准的JavaDoc注释,用于生成API文档;
- @Mapping:这个注解通常与Spring的@Configuration类一起使用,用于映射处理方法到URL路径;
- @Reflective({ControllerMappingReflectiveProcessor.class}):这个注解指示Spring在运行时解析和处理带有此注解的类和方法。
@RequestMapping注解中的各个元素:
- name():返回一个字符串,表示这个映射的名称。如果没有指定,默认为空字符串;
- value():返回一个字符串数组,表示需要映射的URL路径。如果没有指定,默认为空数组。这个路径可以包含占位符,例如/user/{id};
- path():另一个用于指定URL路径的数组。这个数组的值和value()返回的值是一样的,互为别名。
- method():返回一个RequestMethod[]数组,用于指定允许的HTTP请求方法(如GET、POST、PUT、DELETE等)。如果不指定,默认支持所有HTTP方法。
- params():返回一个字符串数组,指定请求必须包含的参数。可以用于限制请求的处理条件,只有当请求中包含指定的参数时,才会调用该方法。
- comsumes():返回一个字符串数组。表示这个映射接受的媒体类型。如果没有指定,默认为空数组,表示接受所有媒体类型。
- produces():返回一个字符串数组,表示这个映射能产生的媒体类型。如果没有指定,默认为空数组,表示能产生所有媒体类型。
- headers():返回一个字符串数组,表示请求头。如果请求头符合数组中的任意一个,那么这个映射就会被触发。
那么如果我们要指定某一种请求方法,那么我们就需要指定@RequestMapping中的Method方法。
假如我们只要该方法只接受GET请求:
java"> @RequestMapping(value = "/sayHi",method = RequestMethod.GET)
完整代码:
java">@RequestMapping("/hello")
@RestController
public class HelloController {@RequestMapping(value = "/sayHi",method = RequestMethod.GET)public String hello(){return "Hello World";}
}
那么我们此时再来看下构建GET和POST请求能不能被接收:
可以看到,当我们指定GET请求后,其他请求就无法被接收,405表示方法不被允许。
请求
我们在访问不同路径的时候,其实就是在发送不同的请求。在发送请求时,可能会带一些参会,我么学习Spring的请求,主要学习如何传递参数到后端以及后端如何接收到参数。
接下来讲的,我们都用postman来构建请求。
传递单个参数
在Spring MVC中,我们可以使用跟传递来的参数相同的名字来获取到参数。
为什么我们可以通过使用与传递来的参数同名的遍历来获取到参数值?
这是基于Spring MVC 的参数绑定机制实现的。
Spring MVC的控制器方法(Controller方法)可以通过多种方式接收请求参数。其中一种常见的方式是直接使用方法参数来接收请求中的数据。Spring MVC会根据请求中的参数名称和控制器方法参数的名称进行匹配,并将请求参数的值绑定到对应的方法参数上。
原理
- 当一个HTTP请求到达Spring MVC的控制器(Controller)方法时,Spring MVC会解析请求中的参数(如URL中的查询参数或表单提交的数据等)。
- Spring MVC会检查控制器方法中的参数列表,将请求参数的名称和方法参数进行匹配。
- 如果请求参数的名称和方法参数的名称一致,Spirng MVC 就自动将请求参数的值赋给对应的方法参数。
示例:
java">@RequestMapping("/param")
@RestController
public class ParamController {@RequestMapping("/v1")public String v1(String message){return "接收到参数 message:"+message;}}
将Spring MVC项目启动后,我们在postman构建以下请求:
那么如果请求中的参数和我们控制器方法中参数名字不一样,后端还能接收到请求中的参数吗?
可以看到,如果我们请求中的参数和我们后端控制器方法中的参数名称不一致,那么我们后端是获取不到请求中参数值的。
有没有什么方法可以解决上面这种情况呢?
后端参数重命名
@RequestParam
在Spring MVC 中,提供了@RequestParam注解,使用这个注解,能够显示指定请求参数的名称,即使方法参数名称不同,也可以正确绑定。
java">@RequestMapping("/param")
@RestController
public class ParamController {@RequestMapping("/v1")public String v1(@RequestParam("mes") String message){return "接收到参数 message:"+message;}}
可以看到,我们即使前后端参数名称不一样,但只要后端对参数进行重命名绑定,也能够获取到请求中的参数。
在绑定后,使用后端名称一致的参数,后端可不可以接收到?我们来试一下:
再看下Spring打印的日志:
这段话的意思就是:请求参数‘mes’不存在。
所以我们可以得出结论:
- 使用@RequestParam 进行参数重命名时,请求参数只能和 @RequestParam 声明的名称一致,才能进行参数绑定和赋值
- 使用 @RequestParam 进行参数重命名时,参数就变成了必传参数。
非必传参数设置
但如果我们实际业务前端的参数是一个非必传参数,那么针对这种情况,我们应该如何解决?
我们看下@RequestParam 注解的源码:
可以看到 required 默认是true,即该注解修饰的参数默认为必传参数。
那么我们可以将这个属性设置为false,就可以避免在没有传递时报错。如下:
java">@RequestMapping("/param")
@RestController
public class ParamController {@RequestMapping("/v1")public String v1(@RequestParam(value = "mes",required = false) String message){return "接收到参数 message:"+message;}}
这里当我们在添加required=false后,mes前面会自动帮我们加key,变成 value="mes"。
这是因为注解属性赋值时,没有指定key的话,默认为value属性,如果需要多个属性进行赋值的时候,就需要写上key。
通过注解@RequestParam,我们就解决了在前后端参数名不一致的情况。
传递多个参数
在后端想要接收多个参数,其实和接收单个参数一样,直接使用方法的参数接收即可,使用多个形参。
java">@RequestMapping("/param")
@RestController
public class ParamController {@RequestMapping("/v2")public String v2(String name,int age){return "接收到参数 name:"+name+",age:"+age;}}
请求中参数的顺序可以跟我们后端控制器方法中参数的顺序不用一致
这里如果我们age不传的话会是什么结果?null还是报错?
我们来试一下:
看到报500错误,那就是我们后端出问题了
我们在上面的代码中,可以看到,如果参数没有传递的话,后端参数默认为null,但这里由于我们接收参数用的是基本类型int,int类型是不能够转换为null的,所以就会报错。它建议我们使用int类型的包装类Integer,这样就能够避免这种情况发生。
java">@RequestMapping("/param")
@RestController
public class ParamController {@RequestMapping("/v2")public String v2(String name,Integer age){return "接收到参数 name:"+name+",age:"+age;}}
所以,我们在接收参数的时候,尽量使用基本数据类型的包装类。
此外,我们需要前后端协商好参数的类型,不能后端认为参数是Integer类型,但前端就给后端传一个String类型的数据,这样会报错。
所以我们需要协商好前后端参数的数据类型,后端获取参数的数据类型和请求的参数数据类型能转换。
传递对象
如果我们的参数比较多时,方法声明就需要有很多的形参,并且后续每增加一个参数,我们也需要修改方法声明,这样很麻烦,我们可以将这些参数封装成一个对象。后续我们想要添加参数的话,就在对象中添加即可,这样降低了代码的耦合性。
当我们Spring MVC 的接收参数是一个对象,Spring MVC在接收到请求后,就会将请求中的参数赋给对象中对应名称的属性。
我们来封装一个userInfo对象:
java">package com.example.demo.Model;public class userInfo {private String name;private Integer age;private String sex;private String address;public void setName(String name) {this.name = name;}public void setAge(Integer age) {this.age = age;}public void setSex(String sex) {this.sex = sex;}public void setAddress(String address) {this.address = address;}public String getName() {return name;}public Integer getAge() {return age;}public String getSex() {return sex;}public String getAddress() {return address;}@Overridepublic String toString() {return "userInfo{" +"name='" + name + '\'' +", age=" + age +", sex='" + sex + '\'' +", address='" + address + '\'' +'}';}
}
并在控制器中定义一个的参数为userInfo类的方法。
java">@RequestMapping("/param")
@RestController
public class ParamController {@RequestMapping("/v3")public String v3(userInfo userInfo){return "接收到参数 userInfo:"+userInfo;}
}
可以看到,Spring会根据参数名称自动绑定到对象的各个属性上,如果某个属性没有传递,则赋值为null(基本类型默认为初始值,如int类型的属性,会被赋值为0)。
传递数组
Spring MVC可以自动绑定数组参数的赋值,即Spring会自动帮我们处理请求参数是数组的情况。
java">@RequestMapping("/param")
@RestController
public class ParamController {@RequestMapping("/v4")public String v4(String[] names){return "接收到参数 names:"+ Arrays.toString(names);}
}
如果一个参数有多个值时,需要用逗号隔开。
当然,我们也可以写成多个参数的形式。
传递集合
和传递数组类似,当请求中的参数有多个相同名称的时,也可以看作集合。
默认情况下,请求中参数名相同的多个值,是封装到数组。但集合我们需要用@RequestParam来进行绑定。
java">@RequestMapping("/param")
@RestController
public class ParamController {@RequestMapping("/v5")public String v5(@RequestParam("list") List<String> list){return "接收到参数 list:"+list+"\n集合里的个数有:"+list.size();}
}
传递JSON格式数据
JSON即 JavaScript Object Notation【JavaScript 对象表示法】
JSON实一种轻量级的数据交互格式,有自己的格式和语法,使用文本表示一个对象或数据的信息。JSON本质是字符串,负责在不同语言中数据传递和交换。
JSON语法格式
数据在键值对(key:value)中
数据用逗号,隔开
对象用{}表示
数组用 [] 表示
值可以是对象,也可以是数组,数组中可以包含多个对象。
JSON的两种结构
- 对象:大括号{}保存的对象是一个无序的键值对集合,一个对象以左括号{开始,右括号}结束。每个“键”后跟一个冒号:,键值对使用逗号,隔开。
- 数组:中括号 [] 保存的数组是值(value)的有序集合,一个数组以中括号[ 开始,右中括号 ] 结束,值之间用逗号,隔开.
因为JSON数据格式是键值对的形式,我们将需要传递的参数封装到Java对象中,然后Spring MVC 参数中使用 @RequestBody 来修饰参数,这样当Spring MVC接收到JSON数据的时候,就会根据Java对象中的属性然后在JSON数据中查找相同名称的键值对,将值赋值给属性。
java">@RequestMapping("/param")
@RestController
public class ParamController {@RequestMapping("/v6")public String v6(@RequestBody userInfo userInfo){return "接收到参数 userInfo:"+userInfo;}
}
如果我们想看一下发送的请求正文的格式是不是JSON,我们可以用Fiddler抓一下包。
获取URL中参数@PathVariable
path variable:路径变量。通过@PathVariable,我们可以实现URL路径上的参数绑定。在控制器方法中,我们在原先@RequstMappinng后要再使用{}来表示要绑定的数据。
示例:
java">@RequestMapping("/param")
@RestController
public class ParamController {@RequestMapping("/v7/{name}/{age}")public String v7(@PathVariable String name,@PathVariable Integer age){return "接收到参数 name:"+name+",age:"+age;}
}
那么如果我们将位置调换一下还能够赋值成功吗?
在上面的代码中,我们如果将参数调换位置,显然不能够赋值成功。
所以我们在构建请求的时候,需要保证前后端数据类型一致。
此外,由@PathVariable修饰的参数默认是必传的,当然也可以设置为非必传,但不仅需要修改@PathVariable的默认值,还需要对路径进行调整。
我们来试下只修改注解默认值。
java">@RequestMapping("/v7/{name}/{age}")public String v7(@PathVariable(value = "name",required = false) String name,@PathVariable Integer age){return "接收到参数 name:"+name+",age:"+age;}
显然,需要再对代码进行补充:
java"> @RequestMapping({"/v7/{name}/{age}","/v7/{age}"})public String v7(@PathVariable(value = "name",required = false) String name,@PathVariable Integer age){return "接收到参数 name:"+name+",age:"+age;}
注意:
- 如果⽅法参数名称和需要绑定的URL中的变量名称⼀致时,可以简写,不⽤给@PathVariable的属性赋值。
- 如果⽅法参数名称和需要绑定的URL中的变量名称不⼀致时,需要@PathVariable的属性value赋值。
上传⽂件@RequestPart
在Spring MVC同样可以接受到请求传来的文件,使用 @RequestPart 修饰既可接收文件。
在Spring MVC中表示文件这种类型需要使用 MultipartFile。
java"> @RequestMapping("/v8")public String v8(@RequestPart MultipartFile file) throws IOException {String fileName = file.getOriginalFilename();file.transferTo(new java.io.File("D:/"+fileName));return "接收到文件:"+fileName;}
那么我们如果在postman传递文件呢?
这样我们就可以构建一个带文件的请求:
先到这里叭,后面的知识点请看下一篇~
以上就是本篇所有内容咯~
若有不足,欢迎指正~