【spring系列】6-SpringMVC入门
【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 应用程序,其结构如下图所示。
MVC 模式将应用程序划分成模型(Model)、视图(View)、控制器(Controller)等三层,如下图所示。
分层 | 描述 |
---|---|
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 模式的不同。
从上图可以看出,三层架构是由表示层(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 的工作流程如下:
- 用户发送请求到服务器;
- 在服务器中,请求被控制层(Controller)接收;
- Controller 调用相应的 Model 层处理请求;
- Model 层处理完毕将结果返回到 Controller;
- Controller 再根据 Model 返回的请求处理结果,找到相应的 View 视图;
- View 视图渲染数据后最终响应给浏览器。
MVC 的优点
MVC 模式具有以下优点:
- 降低代码耦合性:在 MVC 模式中,三层之间相互独立,各司其职。一旦某一层的需求发生了变化,我们就只需要更改相应层中的代码即可,而不会对其他层中的代码造成影响。
- 有利于分工合作:在 MVC 模式中,将应用系统划分成了三个不同的层次,可以更好地实现开发分工。例如,网页设计人员专注于视图(View)层的开发,而那些对业务熟悉的开发人员对 Model 层进行开发,其他对业务不熟悉的开发人员则可以对 Controller 层进行开发。
- 有利于组件的重用:在 MVC 中,多个视图(View)可以共享同一个模型(Model),大大提高了系统中代码的可重用性。
MVC 的不足
MVC 模式存在以下不足之处:
-
增加了系统结构和实现的复杂性:对于简单的应用,如果也严格遵循 MVC 模式,按照模型、视图与控制器对系统进行划分,无疑会增加系统结构的复杂性,并可能产生过多的更新操作,降低运行效率。
-
视图与控制器间的联系过于紧密:虽然视图与控制器是相互分离的,但它们之间联系却是十分紧密的。视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了它们的独立重用。
-
视图对模型数据的低效率访问:视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。
MVC 并不适合小型
甚至中型
规模的项目,花费大量时间将 MVC 应用到规模并不是很大的应用程序中,通常会得不偿失,因此对于 MVC 设计模式的使用要根据具体的应用场景来决定。
SpringMVC入门案例
创建项目顺序
- 创建项目
- 导入jar包
- 创建控制器(Controller),控制URL的走向。
- 创建SpringMVC配置类(类似Spring配置类),目的是为了加载bean对象。
- 初始化SpringMVC环境
- 测试结果
创建Tomcat项目
创建一个Tomcat的项目,我这里使用JDK11,Tomcat9、
导入jar包
SpringMVC基于springframework扩展,我们导入spring-webmvc包和servlet包就可以了。在pom.xml
文件导入以下坐标
1 | <!-- 1. 导入springmvc和servlet坐标--> |
创建Controller类
创建controller控制器类,类似jsp中的servlet类一样。在jsp中,我们需要创建多个servlet的类,就比较复杂,但在springmvc中,一个controller中可以有多个servlet,用注解简化servlet类。
比如,以下,我们在访问http://localhost:8080/springmvc_01_demo_war_exploded/find
和http://localhost:8080/springmvc_01_demo_war_exploded/save
都可以访问到指定的路径。
1 | package site.hikki.controller; |
加载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 | package site.hikki.config; |
初始化SpringMVC环境
测试
打开http://localhost:8080/springmvc_01_demo_war_exploded/find
,查看结果。
总结
注解总结
上面我们使用了几个新的注解,分别是@Controller、@RequestMapping、@ResponseBody。
-
名称:@Controller
-
类型:类注解
-
位置:SpringMVC控制器类定义上方
-
作用:设定SpringMVC的核心控制器bean
-
案例:
1
2
3
4
public class UserController {
}
- 名称:@RequestMapping
- 类型:方法注解
- 位置:lSpringMVC控制器方法定义上方
- 作用:设置当前控制器方法请求访问路径
- 案例:
1 |
|
- 相关属性
- value(默认):请求访问路径
-
名称:@ResponseBody
-
类型:方法注解
-
位置:SpringMVC控制器方法定义上方
-
作用:设置当前控制器方法响应内容为当前返回值,无需解析
-
案例:
1 |
|
方法总结
1. 加载springmMVC容器配置
-
AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类
-
AbstractDispatcherServletInitializer提供三个接口方法供用户实现
- createServletApplicationContext()方法,创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围。
1 | // 加载springmMVC容器配置 |
2. 设置哪些请求归属springMVC处理
-
AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类
-
AbstractDispatcherServletInitializer提供三个接口方法供用户实现
- getServletMappings()方法,设定SpringMVC对应的请求映射路径,设置为/表示拦截所有请求,任意请求都将转入到SpringMVC进行处理
1 | // 设置哪些请求归属springMVC处理 |
3. 加载spring容器配置
-
AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类
-
AbstractDispatcherServletInitializer提供三个接口方法供用户实现
- createRootApplicationContext()方法,如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式同createServletApplicationContext()
1 | // 加载spring容器配置 |
SpringMVC工作流程
容器初始化工作流程
1.初始化web容器
1. 服务器启动,执行ServletContainersInitConfig类,初始化web容器
1 | public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { |
2.创建了WebApplicationContext对象
2. 执行createServletApplicationContext方法,创建了WebApplicationContext对象
1 | // 加载springmMVC容器配置 |
3.加载SpringMvcConfig
3. 加载SpringMvcConfig
在上一步中,创建Web专用的Ioc容器中,注册了SpringMvcConfig类的配置。
1 | // 1.创建springmvc的额配送至文件吗,加载controller对应的bean |
4.加载bean对象
4. 执行@ComponentScan加载对应的bean
加载site.hikki.controller
包下的对bean象。
1 |
5.加载每一个URL控制器
5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法
每一个URL控制器,都使用@RequestMapping
控制着URL执行对应的方法。
1 | // 2.2设置当前操作的访问路径 |
6.设置URL请求走向
6. 执行getServletMappings方法,定义所有的请求都通过SpringMVC
return new String[]{"/"}
定义全部URL都归springmvc管。
1 | // 设置哪些请求归属springMVC处理 |
单次请求工作流程
- 发送请求localhost/save
- web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
- 解析请求路径/save
- 由/save匹配执行对应的方法save()
- 执行save()
- 检测到有@ResponseBody直接将save()方法的返回值作为响应求体返回给请求方
Spring & SpringMVC对应Bean加载
疑问:因为功能不同,如何避免Spring错误的加载到SpringMVC的bean?
在项目中,我们会有多个Bean,比如表现层的bean,控制层的bean,数据层的bean。我们如何控制Spring在加载bean的时候,不加载SpringMVC的bean呢?
Controller加载控制与业务Bean加载控制
-
SpringMVC相关bean(表现层bean)
-
Spring控制的bean
-
业务bean(Service)
-
功能bean(DataSource等)
-
-
SpringMVC相关bean加载控制
- SpringMVC加载的bean对应的包均在com.itheima.controller包内
-
Spring相关bean加载控制
- 方式一:
Spring加载的bean设定扫描范围为site.hikki,排除掉controller包内的bean
- 方式二:
Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等
- 方式三:
不区分Spring与SpringMVC的环境,加载到同一个环境中
- 方式一:
代码实现
我们可以指定扫描范围,精准指定扫描不含controller
的包,需要注意的是,我们在使用mybatis时,是总自定义的bean,但还是建议在扫描时,也将dao
的包添加到扫描范围内,因为可能在某时候,你会重写一些方法时,需要添加到bean对象,就容易报错找不到bean对象,所以,建议除了controller
控制器,其他都添加到扫描范围内。
1 | package site.hikki.config; |
指定整个项目的包都添加到扫描范围内,其中在整个包的扫描范围排除带有@controller
注解的bean。
1 | package site.hikki.config; |
SpringWeb映射路径
以下实验使用的是Apifox客户端进行调试,当然,你用Postman也可以,取决于你的使用习惯。
我们以前在学JSP时,我们每一个映射路径都要用一个Servlet类来表示,这样做让开发效率很低,也很繁琐,SpringMVC中,有给我们提供很好用的映射路径方式,只需要一个注解即可,并且不需要创建多个Servlet类就可以实现,可以在一个类中,设置多个映射路径。
代码实现
我们在之前的学习中,我们也有写过映射路径,如下就是一个简单的映射路径的例子,在方法名上面添加@RequestMapping
注解,值就是映射的路径。
1 | package site.hikki.controller; |
我们访问一下上面的路径试一下,运行项目:
我们仔细观察一下代码,我们设置的映射路径和方法名是可以不一样。
进阶
我们如果想给这些映射路径添加一个前缀
怎么实现?
在SpringMVC控制器类名
上方添加@RequestMapping("student")
就可以了。
1 | package site.hikki.controller; |
总结
-
名称:@RequestMapping
-
类型:方法****注解 类注解
-
位置:SpringMVC控制器方法定义上方
-
作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器方法请求访问路径前缀
-
属性
- value(默认):请求访问路径,或访问路径前缀
-
案例:
1 |
|
参数传递
我们常用的请求参数方式有两种,GET、POST请求。
- GET请求
- POST请求
代码实现
我们使用findByNum
方法来测试接收一个参数打印并返回到客户端。
1 | package site.hikki.controller; |
我们在方法的形参中定义与实参变量名相同
,即可接收到参数。
如果定义的形参与实参变量名不相同,则接受到的值为null,接受参数失败。
这里使用普通的参数传递。
我们在add方法中,在形参中接收了多个参数并打印出来。
1 | package site.hikki.controller; |
传递多个参数时,我使用from表单模拟传输参数
结果如下:
结果看到,带有中文的都乱码了,在以前我们学习的JSP中,我们在Servlet接收参数前都会定义字符集,防止数据乱码,在SpringMVC中,也有这样的一种方法。
POST中文乱码问题
在Servlet容器初始化类中重写getServletFilters
方法,设置字符集为UTF-8
就可以避免中文乱码了。
1 |
|
GET请求中文乱码问题
- 原因:tomcat 8.5版本之后GET请求就不再出现中文乱码问题,但是我们使用的是tomcat7插件,所以会出现GET请求中文乱码问题。
- 解决:在pom.xml添加tomcat7插件处配置UTF-8字符集,解决GET请求中文乱码问题。
1 | <plugins> |
五种类型参数
- 普通参数【重点】
- POJO类型参数(重点)
- 嵌套POJO类型参数(重点)
- 数组类型参数
- 集合类型参数
普通参数:当请求参数名与形参变量名不同,使用@RequestParam绑定参数关系。
1 | //普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系 |
名称 | @RequestParam |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 绑定请求参数与处理器方法形参间的关系 |
参数1:required | 是否为必传参数 |
参数2:defaultValue | 参数默认值 |
POJO类型参数【重点】
POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数。
新建一个Student实体类:
1 | package site.hikki.entity; |
在SpringMVC控制器下新建一个pojo
方法,并设置路径映射,形参使用Student
接收,Student
中定义了四个属性,分别为num、name、age、major,设置get、set方法,并重写toString方法。
1 | //POJO参数传递 |
结果:
在请求参数中,使用Student
一样的变量名就可以自动将参数接收到Student实体类
中,如果在请求的参数中缺少实体类Student
中的哪一个属性,则该属性的值为null
- 嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
1. 新建Grade实体类:
1 | package site.hikki.entity; |
2. 在Student中添加Grade属性:
添加属性后并设置set和get方法,并重写toString方法。
1 | private Grade grade; |
3. 在SpringMVC添加方法
1 | //POJO嵌套POJO参数传递 |
在添加变量时,嵌套的实体类的变量,需要添加前缀,grade.discipline
,前缀grade
为Student
实体类的变量名
**结果:**嵌套POJO可以完全接收,SpringMVC自动将我们的参数自动注入到Grade实体类中。
- 数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数
在SpringMVC控制器下新建一个array
方法,并设置路径映射。形参使用字符数组接收。
1 | //数组类型参数传递 |
结果:
- 集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系
1 | //集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据 |
JSON对象传递
JSON数据参数介绍
- json普通数组([“”,“”,“”,…])
- json对象({key:value,key:value,…})
- json对象数组([{key:value,…},{key:value,…}])
JSON有多种数据类型,但数据传输的方式都是差不多的。
json数据接收步骤
- 添加依赖
- 开启json数据类型自动转换(
@EnableWebMvc
) - 数据实参构造JSON格式
- 在SpringMVC控制器下方法形参添加
@RequestBody
注解
代码实现
1. 添加依赖
1 | <dependency> |
2. 开启自动转换json数据的支持(@EnableWebMvc
)
注意事项:@EnableWebMvc注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换
1 | package site.hikki.config; |
3. 数据实参构造JSON格式
4. 在SpringMVC控制器下方法形参添加@RequestBody
注解
1 | //集合参数:json格式 |
- POJO参数:json数据与形参对象属性名相同,定义POJO类型形参即可接收参数
- 添加依赖
1 | <dependency> |
2. 开启自动转换json数据的支持(@EnableWebMvc
)
注意事项:@EnableWebMvc注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换
1 | package site.hikki.config; |
3. 构造json对象实体类
我这里嵌套了实体类,在Student中嵌套了Grade。
我们在嵌套POJO时,使用{}
括上被嵌套的实体类即可。
4. 在SpringMVC控制器下方法形参添加@RequestBody
注解
1 |
|
结果:
- 添加依赖
1 | <dependency> |
2. 开启自动转换json数据的支持(@EnableWebMvc
)
注意事项:@EnableWebMvc注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换
1 | package site.hikki.config; |
3. 构造json对象数组实体类
在构造json数组时,使用[]
括住数组。
4. 在SpringMVC控制器下方法形参添加@RequestBody
注解
1 | //3. json数组对象嵌套 |
结果:
运行结果显示如下:
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 |
|
@RequestBody注解介绍
- 名称:@RequestBody
- 类型:形参注解
- 位置:SpringMVC控制器方法形参定义前面
- A作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法
只能使用一次
- 范例:
1 |
|
@RequestBody
与@RequestParam
区别
- 区别
@RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】
@RequestBody用于接收json数据【application/json】 - 应用
后期开发中,发送json格式数据为主,@RequestBody应用较广
如果发送非json格式数据,选用@RequestParam接收请求参数
日期参数传递
日期参数传递和其他参数传递类似的,只是日期参数传递要处理一下日期的格式。
请求参数
接收参数
一样是在COntroller
下编写方法,使用形参接收。
其中,接受的日期格式默认是“2023/02/01”
这种格式,则需要使用@DateTimeFormat
注解。
1 | //4. 日期转换 |
@DateTimeFormat注解介绍
-
名称:@DateTimeFormat
-
类型:形参注解
-
位置:SpringMVC控制器方法形参前面
-
作用:设定日期时间型数据格式
-
属性:pattern:指定日期时间格式字符串
3.3 工作原理
- 其内部依赖Converter接口
1 | public interface Converter<S, T> { |
- 请求参数年龄数据(String→Integer)
- json数据转对象(json → POJO)
- 日期格式转换(String → Date)
传递日期类型参数必须在配置类上使用@EnableWebMvc注解。其功能之一:根据类型匹配对应的类型转换器。
响应数据
响应数据一般有多种类型,分别是以下三种:
- 响应页面
- 响应文本数据
- 响应json数据
1 | //1. 响应页面 |
1 | //2. 响应文本数据 |
1. 实体类就哈好比一个json数据,实体类会自动转换成json格式,他依赖于jackson-databind模块的作用。
2. 并且需要开启json自动转化功能,在SpringMvcConfig添加@EnableWebMvc注解
1. 响应POJO对象
1 | //3. 响应POJO类型数据 |
2. 响应POJO列表
1 | //4. 响应POJO集合对象 |
Apifox请求接口文件下载:https://rookie1679.lanzoum.com/iiLGE0me39ze