【spring系列】6-SpringMVC入门

SpringMVC介绍

什么是SpringMVC

SpringMVC=Spring+MVC。

SpringMVC是一个Spring内置的MVC Web框架,英文缩写(Model-View-Controller)。

MVC框架解决了Web开发中常见的问题(参数接收、文件上传、表单验证、国际化等等),而且使用简单,与Spring无缝集成,支持RESTful风格而的URL请求。采用了松散耦合可插播组件结构,比其他MVC框架更具有扩展性和灵活性。

SpringMVC的作用

MVC模式(Model-View-Controller):解决页面代码和后台代码的分离。

MVC模型

通常情况下,一个完整的 Java Web 应用程序,其结构如下图所示。

01-Java Web应用结构-0309303458-0

MVC 模式将应用程序划分成模型(Model)、视图(View)、控制器(Controller)等三层,如下图所示。

02-MVC模式-0309301C8-1

分层 描述
Model(模型) 它是应用程序的主体部分,主要由以下 2 部分组成: 实体类 Bean:专门用来存储业务数据的对象,它们通常与数据库中的某个表对应,例如 User、Student 等。 业务处理 Bean:指 Service 或 Dao 的对象,专门用于处理业务逻辑、数据库访问。 一个模型可以为多个视图(View)提供数据,一套模型(Model)的代码只需写一次就可以被多个视图重用,有效地减少了代码的重复性,增加了代码的可复用性。
View(视图) 指在应用程序中专门用来与浏览器进行交互,展示数据的资源。在 Web 应用中,View 就是我们常说的前台页面,通常由 HTML、JSP、CSS、JavaScript 等组成。
Controller(控制器) 通常指的是,应用程序的 Servlet。它负责将用户的请求交给模型(Model)层进行处理,并将 Model 层处理完成的数据,返回给视图(View)渲染并展示给用户。 在这个过程中,Controller 层不会做任何业务处理,它只是 View(视图)层和 Model (模型)层连接的枢纽,负责调度 View 层和 Model 层,将用户界面和业务逻辑合理的组织在一起,起粘合剂的效果。

MVC模式 VS 三层架构

和 MVC 模式类似,三层架构同样将系统划分成了 3 层:

  • 表示层(UI):用来实现与用户的交互,接收用户请求,并将请求交给业务逻辑层(BLL)和数据访问层(DAL)进行处理,最后将处理结果返回给用户。
  • 业务逻辑层(BLL):起到承上启下的作用,接收表示层传递来的请求,并针对业务对数据进行处理,以实现业务目标。
  • 数据访问层(DAL):用于实现与数据库的交互和访问,例如从数据库中获取数据、保存或修改数据库中的数据等。

虽然三层架构和 MVC 模式一样,都是将应用划分成了 3 层,但它们的划分方式是不同的。

下图展示了三层架构的划分方式,我们可以很清楚地分辨出它与 MVC 模式的不同。

03-三层架构-0309303529-2

从上图可以看出,三层架构是由表示层(UI)、业务逻辑层(BLL)和数据访问层(DAL)三个层次构成的,而 MVC 则是由视图(View)层、控制(Controller)层以及模型(Model)层,且它们之间并不是一一对应的。

三层架构和 MVC 模式中各层对应关系如下:

  • 三层架构中的表示层(UI)包含 HTML、JSP 等前台页面以及后台的 Servlet,即它相当于 MVC 模式中的 View 层 + Controller 层。
  • 三层架构中的业务逻辑层(BLL),则只包含了 Service 接口及其实现类(Servicelmpl)的代码,即它相当于 MVC 模式中 Model 层的一部分,并不包含 Dao 和实体类。
  • 三层架构中的数据访问层(DAL),则只包含了 Dao 接口及其实现类(DaoImpl)的代码,即它相当于 MVC 模式中 Model 层的一部分,并不包含 Service 和实体类。

三层架构将应用中的各个模块划分为表示层(UI)、业务逻辑层(BLL)和数据访问层(DAL)等三层,各层之间采用接口相互访问,并通过实体类作为数据传递的载体。不同的实体类一般对应于数据库中不同的数据表,且实体类的属性与数据库表的字段名一一对应 。

从上面的划分方式来看,三层架构和 MVC 模式确实是不一样的,但从它们的核心来看,两者又是一样的,它们的核心都是“分层、解耦”。

MVC的工作流程

MVC 的工作流程如下:

  1. 用户发送请求到服务器;
  2. 在服务器中,请求被控制层(Controller)接收;
  3. Controller 调用相应的 Model 层处理请求;
  4. Model 层处理完毕将结果返回到 Controller;
  5. Controller 再根据 Model 返回的请求处理结果,找到相应的 View 视图;
  6. View 视图渲染数据后最终响应给浏览器。

MVC 的优点

MVC 模式具有以下优点:

  • 降低代码耦合性:在 MVC 模式中,三层之间相互独立,各司其职。一旦某一层的需求发生了变化,我们就只需要更改相应层中的代码即可,而不会对其他层中的代码造成影响。
  • 有利于分工合作:在 MVC 模式中,将应用系统划分成了三个不同的层次,可以更好地实现开发分工。例如,网页设计人员专注于视图(View)层的开发,而那些对业务熟悉的开发人员对 Model 层进行开发,其他对业务不熟悉的开发人员则可以对 Controller 层进行开发。
  • 有利于组件的重用:在 MVC 中,多个视图(View)可以共享同一个模型(Model),大大提高了系统中代码的可重用性。

MVC 的不足

MVC 模式存在以下不足之处:

  • 增加了系统结构和实现的复杂性:对于简单的应用,如果也严格遵循 MVC 模式,按照模型、视图与控制器对系统进行划分,无疑会增加系统结构的复杂性,并可能产生过多的更新操作,降低运行效率。

  • 视图与控制器间的联系过于紧密:虽然视图与控制器是相互分离的,但它们之间联系却是十分紧密的。视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了它们的独立重用。

  • 视图对模型数据的低效率访问:视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。

MVC 并不适合小型甚至中型规模的项目,花费大量时间将 MVC 应用到规模并不是很大的应用程序中,通常会得不偿失,因此对于 MVC 设计模式的使用要根据具体的应用场景来决定。

SpringMVC入门案例

创建项目顺序

  1. 创建项目
  2. 导入jar包
  3. 创建控制器(Controller),控制URL的走向。
  4. 创建SpringMVC配置类(类似Spring配置类),目的是为了加载bean对象。
  5. 初始化SpringMVC环境
  6. 测试结果

创建Tomcat项目

创建一个Tomcat的项目,我这里使用JDK11,Tomcat9、

04-新建模块20230130-801

导入jar包

SpringMVC基于springframework扩展,我们导入spring-webmvc包和servlet包就可以了。在pom.xml文件导入以下坐标

1
2
3
4
5
6
7
8
9
10
11
12
<!--    1. 导入springmvc和servlet坐标-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>

创建Controller类

创建controller控制器类,类似jsp中的servlet类一样。在jsp中,我们需要创建多个servlet的类,就比较复杂,但在springmvc中,一个controller中可以有多个servlet,用注解简化servlet类。

比如,以下,我们在访问http://localhost:8080/springmvc_01_demo_war_exploded/findhttp://localhost:8080/springmvc_01_demo_war_exploded/save都可以访问到指定的路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package site.hikki.controller;

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

//2. 定义controller
//2.1使用controller定义bean
@Controller
public class UserController {
// 2.2设置当前操作的访问路径
@RequestMapping("/save")
// 2.3 设置当前操作的返回类型
@ResponseBody
public String save(){
System.out.println("UserController save ...");
return "{'name':'blog.hikki.site'}";
}
@RequestMapping("/find")
@ResponseBody
public String find(){
System.out.println("UserController find ...");
return "{'name':'https://blog.hikki.site'}";
}
}

加载SpringMVC的bean对象

加载SpringMVC对应的bean,使用@ComponentScan注解扫描bean,和Spring一样,都需要扫描bean的对象。

回顾Spring的知识点

回顾Spring的知识点

配置当前类是bean对象,有三个注解都可以实现,三个注解表示的意思是一样的,我们常常将他们区分开始为了更好的理解代码,理解当前的bean是作用,提高开发效率。

  • @Controller:控制器(注入服务)。
  • @Repository:dao持久层(实现dao访问),标注数据访问组件。
  • @Service:service服务层(注入dao),处理业务逻辑。
  • @component: 标注一个类为Spring容器的Bean,(把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>

使用@Controller表示当前类是bean对象

1
2
3
4
5
6
7
8
9
10
package site.hikki.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

// 1.创建springmvc的额配送至文件吗,加载controller对应的bean
@ComponentScan("site.hikki.controller")
@Configuration
public class SpringMvcConfig {
}

初始化SpringMVC环境

测试

打开http://localhost:8080/springmvc_01_demo_war_exploded/find,查看结果。

05-Mozilla Firefox20230130-283

总结

注解总结

上面我们使用了几个新的注解,分别是@Controller、@RequestMapping、@ResponseBody。

  • 名称:@Controller

  • 类型:类注解

  • 位置:SpringMVC控制器类定义上方

  • 作用:设定SpringMVC的核心控制器bean

  • 案例:

    1
    2
    3
    4
    @Controller
    public class UserController {

    }
  • 名称:@RequestMapping
  • 类型:方法注解
  • 位置:lSpringMVC控制器方法定义上方
  • 作用:设置当前控制器方法请求访问路径
  • 案例:
1
2
3
4
@RequestMapping("/save")
public void save(){
System.out.println("user save ...");
}
  • 相关属性
    • value(默认):请求访问路径
  • 名称:@ResponseBody

  • 类型:方法注解

  • 位置:SpringMVC控制器方法定义上方

  • 作用:设置当前控制器方法响应内容为当前返回值,无需解析

  • 案例:

1
2
3
4
5
6
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'info':'springmvc'}";
}

方法总结

1. 加载springmMVC容器配置

  • AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类

  • AbstractDispatcherServletInitializer提供三个接口方法供用户实现

    • createServletApplicationContext()方法,创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围。
1
2
3
4
5
6
//    加载springmMVC容器配置
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}

2. 设置哪些请求归属springMVC处理

  • AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类

  • AbstractDispatcherServletInitializer提供三个接口方法供用户实现

    • getServletMappings()方法,设定SpringMVC对应的请求映射路径,设置为/表示拦截所有请求,任意请求都将转入到SpringMVC进行处理
1
2
3
4
//    设置哪些请求归属springMVC处理
protected String[] getServletMappings() {
return new String[]{"/"}; //表示所有请求都归springmvc管
}

3. 加载spring容器配置

  • AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类

  • AbstractDispatcherServletInitializer提供三个接口方法供用户实现

    • createRootApplicationContext()方法,如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式同createServletApplicationContext()
1
2
3
4
//    加载spring容器配置
protected WebApplicationContext createRootApplicationContext() {
return null;
}

SpringMVC工作流程

容器初始化工作流程

1.初始化web容器

1. 服务器启动,执行ServletContainersInitConfig类,初始化web容器

1
2
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
}
2.创建了WebApplicationContext对象

2. 执行createServletApplicationContext方法,创建了WebApplicationContext对象

1
2
3
4
5
6
//    加载springmMVC容器配置
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
3.加载SpringMvcConfig

3. 加载SpringMvcConfig

在上一步中,创建Web专用的Ioc容器中,注册了SpringMvcConfig类的配置。

1
2
3
4
5
// 1.创建springmvc的额配送至文件吗,加载controller对应的bean
@ComponentScan("site.hikki.controller")
@Configuration
public class SpringMvcConfig {
}
4.加载bean对象

4. 执行@ComponentScan加载对应的bean

加载site.hikki.controller包下的对bean象。

1
@ComponentScan("site.hikki.controller")
5.加载每一个URL控制器

5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法

每一个URL控制器,都使用@RequestMapping控制着URL执行对应的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//    2.2设置当前操作的访问路径
@RequestMapping("/save")
// 2.3 设置当前操作的返回类型
@ResponseBody
public String save(){
System.out.println("UserController save ...");
return "{'name':'blog.hikki.site'}";
}

@RequestMapping("/find")
@ResponseBody
public String find(){
System.out.println("UserController find ...");
return "{'name':'https://blog.hikki.site'}";
}
6.设置URL请求走向

6. 执行getServletMappings方法,定义所有的请求都通过SpringMVC

return new String[]{"/"}定义全部URL都归springmvc管。

1
2
3
4
//    设置哪些请求归属springMVC处理
protected String[] getServletMappings() {
return new String[]{"/"}; //表示所有请求都归springmvc管
}

06-启动服务器初始化过程-20230131193122709

单次请求工作流程

  1. 发送请求localhost/save
  2. web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
  3. 解析请求路径/save
  4. 由/save匹配执行对应的方法save()
  5. 执行save()
  6. 检测到有@ResponseBody直接将save()方法的返回值作为响应求体返回给请求方

07-单次请求工作流程-20230104193327981

Spring & SpringMVC对应Bean加载

疑问:因为功能不同,如何避免Spring错误的加载到SpringMVC的bean?

在项目中,我们会有多个Bean,比如表现层的bean,控制层的bean,数据层的bean。我们如何控制Spring在加载bean的时候,不加载SpringMVC的bean呢?

Controller加载控制与业务Bean加载控制

  • SpringMVC相关bean(表现层bean)

  • Spring控制的bean

    1. 业务bean(Service)

    2. 功能bean(DataSource等)

  • SpringMVC相关bean加载控制

    1. SpringMVC加载的bean对应的包均在com.itheima.controller包内
  • Spring相关bean加载控制

    1. 方式一:Spring加载的bean设定扫描范围为site.hikki,排除掉controller包内的bean
    2. 方式二:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等
    3. 方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中

代码实现

我们可以指定扫描范围,精准指定扫描不含controller的包,需要注意的是,我们在使用mybatis时,是总自定义的bean,但还是建议在扫描时,也将dao的包添加到扫描范围内,因为可能在某时候,你会重写一些方法时,需要添加到bean对象,就容易报错找不到bean对象,所以,建议除了controller控制器,其他都添加到扫描范围内。

1
2
3
4
5
6
7
8
9
package site.hikki.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan({"site.hikki.dao","site.hikki.service"}) // 方法一
public class SpringConfig {
}

指定整个项目的包都添加到扫描范围内,其中在整个包的扫描范围排除带有@controller注解的bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package site.hikki.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

@Configuration
@ComponentScan(value="site.hikki", // 方法二
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {
}

SpringWeb映射路径

以下实验使用的是Apifox客户端进行调试,当然,你用Postman也可以,取决于你的使用习惯。

我们以前在学JSP时,我们每一个映射路径都要用一个Servlet类来表示,这样做让开发效率很低,也很繁琐,SpringMVC中,有给我们提供很好用的映射路径方式,只需要一个注解即可,并且不需要创建多个Servlet类就可以实现,可以在一个类中,设置多个映射路径。

代码实现

我们在之前的学习中,我们也有写过映射路径,如下就是一个简单的映射路径的例子,在方法名上面添加@RequestMapping注解,值就是映射的路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package site.hikki.controller;

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

@Controller
public class ControllerStudent {
@RequestMapping("find")
@ResponseBody
public String findAll(){
System.out.println("ControllerStudent findAll..");
return "return ControllerStudent findAll..";
}

@RequestMapping("add")
@ResponseBody
public String add(){
System.out.println("ControllerStudent add..");
return "return ControllerStudent add..";
}
}

我们访问一下上面的路径试一下,运行项目:

01-映射路径-SpringBoot20230201-113

我们仔细观察一下代码,我们设置的映射路径和方法名是可以不一样。

进阶

我们如果想给这些映射路径添加一个前缀怎么实现?

SpringMVC控制器类名上方添加@RequestMapping("student")就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package site.hikki.controller;

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

@Controller
@RequestMapping("student")
public class ControllerStudent {
@RequestMapping("find")
@ResponseBody
public String findAll(){
System.out.println("ControllerStudent findAll..");
return "return ControllerStudent findAll..";
}

@RequestMapping("add")
@ResponseBody
public String add(){
System.out.println("ControllerStudent add..");
return "return ControllerStudent add..";
}
}

02-映射路径-SpringBoot20230201-515

总结

  • 名称:@RequestMapping

  • 类型:方法****注解 类注解

  • 位置:SpringMVC控制器方法定义上方

  • 作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器方法请求访问路径前缀

  • 属性

    • value(默认):请求访问路径,或访问路径前缀
  • 案例:

1
2
3
4
5
6
7
8
9
10
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'module':'user save'}";
}
}

参数传递

我们常用的请求参数方式有两种,GET、POST请求。

  • GET请求
  • POST请求

代码实现

我们使用findByNum方法来测试接收一个参数打印并返回到客户端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package site.hikki.controller;

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

@Controller
@RequestMapping("student")
public class ControllerStudent {

@RequestMapping("findByNum")
@ResponseBody
public String findByNum(Integer num){
System.out.println("findByNum===>"+num);
return "findByNum===>"+num;
}

@RequestMapping("find")
@ResponseBody
public String findAll(){
System.out.println("ControllerStudent findAll..");
return "return ControllerStudent findAll..";
}

@RequestMapping("add")
@ResponseBody
public String add(){
System.out.println("ControllerStudent add..");
return "return ControllerStudent add..";
}
}

我们在方法的形参中定义与实参变量名相同,即可接收到参数。

如果定义的形参与实参变量名不相同,则接受到的值为null,接受参数失败。

这里使用普通的参数传递。

03-参数传递-SpringBoot20230201-378

我们在add方法中,在形参中接收了多个参数并打印出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package site.hikki.controller;

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

@Controller
@RequestMapping("student")
public class ControllerStudent {

@RequestMapping("findByNum")
@ResponseBody
public String findByNum(Integer num){
System.out.println("findByNum===>"+num);
return "findByNum===>"+num;
}

@RequestMapping("find")
@ResponseBody
public String findAll(){
System.out.println("ControllerStudent findAll..");
return "return ControllerStudent findAll..";
}

@RequestMapping("add")
@ResponseBody
public String add(Integer num,String name,String age,String major){
System.out.println("findByNum===>"+num);
System.out.println("name===>"+name);
System.out.println("age===>"+age);
System.out.println("major===>"+major);
return "return ControllerStudent add..";
}
}

传递多个参数时,我使用from表单模拟传输参数

04-参数传递-多个参数-SpringBoot20230201-776

结果如下:

05-参数传递-多个参数结果-springmvc – Controll20230201-448

结果看到,带有中文的都乱码了,在以前我们学习的JSP中,我们在Servlet接收参数前都会定义字符集,防止数据乱码,在SpringMVC中,也有这样的一种方法。

POST中文乱码问题

在Servlet容器初始化类中重写getServletFilters方法,设置字符集为UTF-8就可以避免中文乱码了。

1
2
3
4
5
6
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}

06-解决POST请求中文乱码问题-springmvc – ServletC20230201-653

GET请求中文乱码问题

  • 原因:tomcat 8.5版本之后GET请求就不再出现中文乱码问题,但是我们使用的是tomcat7插件,所以会出现GET请求中文乱码问题。
  • 解决:在pom.xml添加tomcat7插件处配置UTF-8字符集,解决GET请求中文乱码问题。
1
2
3
4
5
6
7
8
9
10
11
12
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>9090</port>
<path>/</path> <!--项目访问路径。当前配置的访问是localhost:9090/, 如果配置是/aa,则访问路径为localhost:9090/aa -->
<uriEncoding>UTF-8</uriEncoding><!--访问路径编解码字符集-->
</configuration>
</plugin>
</plugins>

五种类型参数

  • 普通参数【重点】
  • POJO类型参数(重点)
  • 嵌套POJO类型参数(重点)
  • 数组类型参数
  • 集合类型参数

普通参数:当请求参数名与形参变量名不同,使用@RequestParam绑定参数关系。

01-普通参数-SpringBoot20230201-758

1
2
3
4
5
6
7
//普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系
@RequestMapping("findByNum")
@ResponseBody
public String findByNum(@RequestParam("num") Integer Studentnum){
System.out.println("findByNum===>"+Studentnum);
return "findByNum===>"+Studentnum;
}
名称 @RequestParam
类型 形参注解
位置 SpringMVC控制器方法形参定义前面
作用 绑定请求参数与处理器方法形参间的关系
参数1:required 是否为必传参数
参数2:defaultValue 参数默认值

POJO类型参数【重点】

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

新建一个Student实体类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package site.hikki.entity;

public class Student {
private Integer num;
private String name;
private String age;
private String major;

@Override
public String toString() {
return "Student{" +
"num=" + num +
", name='" + name + '\'' +
", age='" + age + '\'' +
", major='" + major + '\'' +
'}';
}


public Integer getNum() {
return num;
}

public void setNum(Integer num) {
this.num = num;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAge() {
return age;
}

public void setAge(String age) {
this.age = age;
}

public String getMajor() {
return major;
}

public void setMajor(String major) {
this.major = major;
}
}

02-POJO参数-SpringBoot20230201-017

在SpringMVC控制器下新建一个pojo方法,并设置路径映射,形参使用Student接收,Student中定义了四个属性,分别为num、name、age、major,设置get、set方法,并重写toString方法。

1
2
3
4
5
6
7
8
//POJO参数传递
//嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递
@RequestMapping("pojo")
@ResponseBody
public String pojo(Student student){
System.out.println(student);
return "student===>"+student;
}

结果:

在请求参数中,使用Student一样的变量名就可以自动将参数接收到Student实体类中,如果在请求的参数中缺少实体类Student中的哪一个属性,则该属性的值为null

03-POJO参数传递结果-springmvc – Controll20230201-253

  • 嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数

1. 新建Grade实体类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package site.hikki.entity;

public class Grade {
private String discipline;
private double score;

@Override
public String toString() {
return "Grade{" +
"discipline='" + discipline + '\'' +
", score=" + score +
'}';
}

public String getDiscipline() {
return discipline;
}

public void setDiscipline(String discipline) {
this.discipline = discipline;
}

public double getScore() {
return score;
}

public void setScore(double score) {
this.score = score;
}
}

2. 在Student中添加Grade属性:

添加属性后并设置set和get方法,并重写toString方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private Grade grade;

@Override
public String toString() {
return "Student{" +
"num=" + num +
", name='" + name + '\'' +
", age='" + age + '\'' +
", major='" + major + '\'' +
", grade=" + grade +
'}';
}

public Grade getGrade() {
return grade;
}

public void setGrade(Grade grade) {
this.grade = grade;
}

3. 在SpringMVC添加方法

1
2
3
4
5
6
7
//POJO嵌套POJO参数传递
@RequestMapping("pojoinpojo")
@ResponseBody
public String pojoinpojo(Student student){
System.out.println(student);
return "student===>"+student;
}

在添加变量时,嵌套的实体类的变量,需要添加前缀,grade.discipline,前缀gradeStudent实体类的变量名

04-pojo嵌套参数传递-SpringBoot20230201-648

**结果:**嵌套POJO可以完全接收,SpringMVC自动将我们的参数自动注入到Grade实体类中。

05-pojo嵌套参数传递结果-springmvc – Controll20230201-161

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

06-数组参数传递-SpringBoot20230201-938

在SpringMVC控制器下新建一个array方法,并设置路径映射。形参使用字符数组接收。

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

结果:

07-数组参数传递结果-springmvc – Controll20230201-893

  • 集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系

08-集合参数传递-SpringBoot20230201-279

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

09-集合参数传递-结果-springmvc – Controll20230201-281

JSON对象传递

JSON数据参数介绍

  • json普通数组([“”,“”,“”,…])
  • json对象({key:value,key:value,…})
  • json对象数组([{key:value,…},{key:value,…}])

JSON有多种数据类型,但数据传输的方式都是差不多的。

json数据接收步骤

  1. 添加依赖
  2. 开启json数据类型自动转换(@EnableWebMvc)
  3. 数据实参构造JSON格式
  4. 在SpringMVC控制器下方法形参添加@RequestBody注解

代码实现

1. 添加依赖

1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>

2. 开启自动转换json数据的支持(@EnableWebMvc)

注意事项:@EnableWebMvc注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换

1
2
3
4
5
6
7
8
9
10
11
package site.hikki.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan(value = "site.hikki")
@EnableWebMvc //开启自动转换json数据的支持
public class SpringMvcConfig {
}

3. 数据实参构造JSON格式

01-json参数-SpringBoot20230201-326

4. 在SpringMVC控制器下方法形参添加@RequestBody注解

1
2
3
4
5
6
7
8
9
//集合参数:json格式
//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
//2.使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据
@RequestMapping("/json")
@ResponseBody
public String json(@RequestBody List<String> likes){
System.out.println("集合参数传递 likes ==> "+ likes);
return "{'likes':'"+ likes +"'}";
}
  • POJO参数:json数据与形参对象属性名相同,定义POJO类型形参即可接收参数
  1. 添加依赖
1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>

2. 开启自动转换json数据的支持(@EnableWebMvc)

注意事项:@EnableWebMvc注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换

1
2
3
4
5
6
7
8
9
10
11
package site.hikki.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan(value = "site.hikki")
@EnableWebMvc //开启自动转换json数据的支持
public class SpringMvcConfig {
}

3. 构造json对象实体类

我这里嵌套了实体类,在Student中嵌套了Grade。

02-json对象实体类-SpringBoot20230201-700

我们在嵌套POJO时,使用{}括上被嵌套的实体类即可。

4. 在SpringMVC控制器下方法形参添加@RequestBody注解

1
2
3
4
5
6
@RequestMapping("/jsonobject")
@ResponseBody
public String jsonObject(@RequestBody Student student){
System.out.println("json参数传递 student ==> "+ student);
return "{'student':'"+ student +"'}";
}

结果:

03-json对象实体类结果-springmvc – Controll20230201-301

  1. 添加依赖
1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>

2. 开启自动转换json数据的支持(@EnableWebMvc)

注意事项:@EnableWebMvc注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换

1
2
3
4
5
6
7
8
9
10
11
package site.hikki.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan(value = "site.hikki")
@EnableWebMvc //开启自动转换json数据的支持
public class SpringMvcConfig {
}

3. 构造json对象数组实体类

04-json对象数组-SpringBoot20230201-134

在构造json数组时,使用[]括住数组。

4. 在SpringMVC控制器下方法形参添加@RequestBody注解

1
2
3
4
5
6
7
//3. json数组对象嵌套
@RequestMapping("/jsonobjectarray")
@ResponseBody
public String jsonObjectArray(@RequestBody List<Student> student){
System.out.println("json参数传递 student ==> "+ student);
return "{'student':'"+ student +"'}";
}

结果:

运行结果显示如下:

1
json参数传递 student ==> [Student{num=2040811177, name='小码同学', age='20', major='计算机科学与技术', grade=Grade{discipline='Java程序设计', score=90.5}}, Student{num=2040811178, name='吴小白', age='20', major='计算机科学与技术', grade=Grade{discipline='Java程序设计', score=60.5}}]

总结

@EnableWebMvc注解介绍

  • 名称:@EnableWebMvc
  • 类型:配置类注解
  • 位置:SpringMVC配置类定义上方
  • 作用:开启SpringMVC多项辅助功能
  • 范例:
1
2
3
4
5
@Configuration
@ComponentScan("site.hikki.controller")
@EnableWebMvc
public class SpringMvcConfig {
}

@RequestBody注解介绍

  • 名称:@RequestBody
  • 类型:形参注解
  • 位置:SpringMVC控制器方法形参定义前面
  • A作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次
  • 范例:
1
2
3
4
5
6
@RequestMapping("/json")
@ResponseBody
public String json(@RequestBody List<String> likes){
System.out.println("json参数传递 likes ==> "+ likes);
return "{'likes':'"+ likes +"'}";
}

@RequestBody@RequestParam区别

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

日期参数传递

日期参数传递和其他参数传递类似的,只是日期参数传递要处理一下日期的格式。

请求参数

07-日期参数传递-SpringBoot20230201-749

接收参数

一样是在COntroller下编写方法,使用形参接收。

其中,接受的日期格式默认是“2023/02/01”这种格式,则需要使用@DateTimeFormat注解。

1
2
3
4
5
6
7
8
9
10
11
//4. 日期转换
@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'}";
}

@DateTimeFormat注解介绍

  • 名称:@DateTimeFormat

  • 类型:形参注解

  • 位置:SpringMVC控制器方法形参前面

  • 作用:设定日期时间型数据格式

  • 属性:pattern:指定日期时间格式字符串

3.3 工作原理

  • 其内部依赖Converter接口
1
2
3
4
public interface Converter<S, T> {
@Nullable
T convert(S var1);
}
  • 请求参数年龄数据(String→Integer)
  • json数据转对象(json → POJO)
  • 日期格式转换(String → Date)

传递日期类型参数必须在配置类上使用@EnableWebMvc注解。其功能之一:根据类型匹配对应的类型转换器。

响应数据

响应数据一般有多种类型,分别是以下三种:

  1. 响应页面
  2. 响应文本数据
  3. 响应json数据
1
2
3
4
5
6
//1. 响应页面
@RequestMapping("/toStudent")
public String toPage(){
System.out.println("响应student.jsp页面");
return "student.jsp";
}
1
2
3
4
5
6
7
//2. 响应文本数据
@RequestMapping("/toText")
@ResponseBody
public String toText(){
System.out.println("响应文本数据");
return "response Text";
}

1. 实体类就哈好比一个json数据,实体类会自动转换成json格式,他依赖于jackson-databind模块的作用。
2. 并且需要开启json自动转化功能,在SpringMvcConfig添加@EnableWebMvc注解

1. 响应POJO对象

1
2
3
4
5
6
7
8
9
10
11
12
//3. 响应POJO类型数据
@RequestMapping("/toPOJO")
@ResponseBody
public Student toEntity(){
System.out.println("响应实体类,也就是POJO");
Student student=new Student();
student.setName("小码同学");
student.setAge("20");
student.setMajor("计算机科学与技术");
student.setNum(2040811177);
return student;
}

2. 响应POJO列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//4. 响应POJO集合对象
@RequestMapping("/toEntityList")
@ResponseBody
public List<Student> toEntityList(){
Student student = new Student();
student.setNum(2040811177);
student.setName("小码同学");
student.setAge("20");
student.setMajor("计算机科学与技术");

Student student1 = new Student();
student1.setNum(2040800000);
student1.setName("吴小白");
student1.setAge("28");
student1.setMajor("物联网");
ArrayList<Student> list = new ArrayList<>();
list.add(student);
list.add(student1);
return list;
}

Apifox请求接口文件下载:https://rookie1679.lanzoum.com/iiLGE0me39ze