SpringMVC零基础入门 - 概述、入门搭建、PostMan的使用(常见数据类型的传输)、REST风格编程
- SpringMVC是隶属于Spring框架的一部分,主要是用来进行Web开发,是对Servlet进行了封装
- SpringMVC是处于Web层的框架,所以其主要的作用就是用来接收前端发过来的请求和数据然后经过处理并将处理的结果响应给前端,所以如何处理请求和响应是SpringMVC中非常重要的一块内容。
- SSM整合:SpringMVC+Spring+Mybatis整合在一起,三个框架的一个综合应用。
文章目录
- [SpringMVC零基础入门 - 概述、入门搭建、PostMan的使用(常见数据类型的传输)、REST风格编程](#SpringMVC零基础入门 - 概述、入门搭建、PostMan的使用(常见数据类型的传输)、REST风格编程)
-
- [1 SpringMVC概述](#1 SpringMVC概述)
- [2 SpringMVC入门案例](#2 SpringMVC入门案例)
-
- [2.1 Servlet开发](#2.1 Servlet开发)
- [2.2 案例制作](#2.2 案例制作)
- [2.3 案例总结](#2.3 案例总结)
-
- [2.3.1 工作类型](#2.3.1 工作类型)
- [2.3.2 工作流程解析](#2.3.2 工作流程解析)
-
- [2.3.2.1 启动服务器初始化过程](#2.3.2.1 启动服务器初始化过程)
- [2.3.2.2 单次请求过程](#2.3.2.2 单次请求过程)
- [2.4 bean加载控制](#2.4 bean加载控制)
-
- [2.4.1 问题分析](#2.4.1 问题分析)
- [2.4.2 设置bean加载控制](#2.4.2 设置bean加载控制)
- [3 PostMan工具的使用](#3 PostMan工具的使用)
-
- [3.1 下载与安装](#3.1 下载与安装)
- [3.2 请求与响应](#3.2 请求与响应)
-
- [3.2.1 设置请求映射路径](#3.2.1 设置请求映射路径)
- [3.2.2 请求参数](#3.2.2 请求参数)
-
- [3.2.2.1 GET](#3.2.2.1 GET)
- [3.2.2.2 GET乱码](#3.2.2.2 GET乱码)
- [3.2.2.3 POST](#3.2.2.3 POST)
- [3.2.2.4 POST乱码](#3.2.2.4 POST乱码)
- [3.2.3 五种类型参数传递](#3.2.3 五种类型参数传递)
-
- [3.2.3.1 普通参数](#3.2.3.1 普通参数)
- [3.2.3.2 POJO类型参数](#3.2.3.2 POJO类型参数)
- [3.2.3.3 嵌套POJO类型参数](#3.2.3.3 嵌套POJO类型参数)
- [3.2.3.4 数组类型参数](#3.2.3.4 数组类型参数)
- [3.2.3.5 集合类型参数](#3.2.3.5 集合类型参数)
- [3.2.4 JSON数据传输参数](#3.2.4 JSON数据传输参数)
-
- [3.2.4.1 JSON普通数组](#3.2.4.1 JSON普通数组)
- [3.2.4.2 JSON对象](#3.2.4.2 JSON对象)
- [3.2.4.3 JSON对象数组](#3.2.4.3 JSON对象数组)
- [3.2.4.4 小结](#3.2.4.4 小结)
- [3.2.5 日期类型参数传递](#3.2.5 日期类型参数传递)
- [3.2.6 响应](#3.2.6 响应)
- [4 Rest风格](#4 Rest风格)
-
- [4.1 REST简介](#4.1 REST简介)
- [4.2 RESTful入门](#4.2 RESTful入门)
-
- [4.2.1 行为动作的确定](#4.2.1 行为动作的确定)
- [4.2.2 携带参数](#4.2.2 携带参数)
- [4.2.3 小结](#4.2.3 小结)
- [4.2.4 接受参数的注解](#4.2.4 接受参数的注解)
- [4.2.4 接受参数的注解](#4.2.4 接受参数的注解)
1 SpringMVC概述
现在web程序大都基于三层架构来实现
-
浏览器发送一个请求给后端服务器,后端服务器现在是使用Servlet来接收请求和数据
-
如果所有的处理都交给Servlet来处理的话,所有的东西都耦合在一起,对后期的维护和扩展极为不利
-
将后端服务器Servlet拆分成三层,分别是web、service和dao
- web层主要由servlet来处理,负责页面请求和数据的收集以及响应结果给前端
- service层主要负责业务逻辑的处理
- dao层主要负责数据的增删改查操作
-
servlet处理请求和数据的时候,存在的问题是一个servlet只能处理一个请求
-
针对web层进行了优化,采用了MVC设计模式,将其设计为
controller
、view
和Model
-
controller负责请求和数据的接收,接收后将其转发给service进行业务处理
-
service根据需要会调用dao对数据进行增删改查
-
dao把数据处理完后将结果交给service, service再交给controller
-
controller根据需求组装成Model(Model 的组成部分包括 Service 层和 DAO 层)和View,Model和View组合起来生成页面转发给前端浏览器
-
这样做的好处就是controller可以处理多个请求,并对请求进行分发,执行不同的业务操作。
MVC设计模式中,为什么controller可以处理多个请求,而servlet处理请求和数据的时候,一个servlet只能处理一个请求?
:总的来说,MVC的Controller是一个设计模式,而Servlet是Java中处理服务器端请求的一种技术
Controller的设计目标是将请求路由到适当的处理程序,而不是处理具体的数据和业务逻辑
Servlet是Java平台上的一种服务器端组件 ,用于处理客户端请求。每个Servlet类实例处理一个请求,这是因为Servlet是基于Java的服务器端技术,每个请求都会导致一个新的线程或处理过程来处理该请求。每个Servlet实例独立处理一个请求,这确保了线程安全性
-
上面的模式因为是同步调用,性能慢慢的跟不上需求,所以**异步调用慢慢的走到了前台**,是现在比较流行的一种处理方式。
-
因为是异步调用,所以后端不需要返回view视图,将其去除
为什么?
:异步调用后端通常涉及前端通过Ajax(Asynchronous JavaScript and XML)或其他异步技术向后端发送请求,而后端在收到请求后进行处理,然后将结果以某种数据格式返回给前端,而不是返回整个HTML视图。
- 前端如果通过异步调用的方式进行交互,后台就需要将返回的数据转换成json格式进行返回
- SpringMVC主要负责的就是
- controller如何接收请求和数据
- 如何将请求和数据转发给业务层
- 如何将响应数据转换成json发回到前端
综上:
-
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
-
优点
- 使用简单、开发便捷(相比于Servlet)
- 灵活性强
2 SpringMVC入门案例
2.1 Servlet开发
因为SpringMVC是一个Web框架,需要我们提供Servlet(将来是要替换Servlet)
用Servlet是如何进行开发的
回顾:用Servlet是如何进行开发的
-
创建web工程(Maven结构)
-
设置tomcat服务器,加载web工程(tomcat插件)
-
导入坐标(Servlet)
-
定义处理请求的功能类(UserServlet)
-
设置请求映射(配置映射关系)
SpringMVC的制作过程和上述流程几乎是一致的,具体的实现流程:
-
创建web工程(Maven结构)
-
设置tomcat服务器,加载web工程(tomcat插件)
-
导入坐标(SpringMVC+Servlet)
-
定义处理请求的功能类(UserController)
-
设置请求映射(配置映射关系)
-
将SpringMVC设定加载到Tomcat容器中
2.2 案例制作
步骤1: 创建Maven项目
打开IDEA,创建一个新的web项目
① 使用骨架(archetype):
maven项目的三个坐标,可采用默认,也可自己修改
点击创建后,会自动从阿里云下载包:
下载完成:
如果发现创建的模块位置不对,可以在refactor里面移动:
修改路径:(在这里删掉后面即可)
如果消失了,通过maven导入的方式导入即可:
进行一些修改与添加:
1 在依赖中选择打包方式:war
2 默认没有java和resources目录,需要手动完成创建补齐,最终的目录结果如下
② 不使用骨架:
剩下的就是修改打包方式和补全相关目录了
步骤2:导入jar包
将pom.xml中多余的内容删除掉,再添加SpringMVC需要的依赖
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baidu</groupId>
<artifactId>springmvc_01_quickstart</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
回顾: servlet的坐标为什么需要添加<scope>provided</scope>
?
-
scope是maven中jar包依赖作用范围的描述,
-
如果不设置默认是
compile
在在编译、运行、测试时均有效 -
如果运行有效的话就会和tomcat中的servlet-api包发生冲突,导致启动报错
-
provided代表的是该包只在编译和测试的时候用,运行的时候无效直接使用tomcat中的,就避免冲突
springmvc依赖和servlet依赖和插件
-
Spring Web MVC依赖: 这个依赖用于引入整个Spring Web MVC框架。它包含了在构建基于Spring MVC的Web应用程序时所需的所有Spring框架的相关库和功能。
SpringMVC是基于Spring的,在pom.xml只导入了
spring-webmvc
jar包的原因是它会自动依赖spring相关坐标 -
Java Servlet API依赖: 这个依赖用于引入Java Servlet API规范。它只包含了Java Servlet规范中定义的类和接口,用于支持Servlet的开发 。通常,这个依赖的scope被设置为
provided
,因为Servlet容器(即我们这个实例中的tomcat )已经包含了这个API,而且在部署时会由Servlet容器提供。 -
tomcat7-maven-plugin插件 (也可以用maven-compiler-plugin插件 ):定义和配置Java编译器的行为。插件可以帮助你指定编译源代码的Java版本,以及生成的字节码文件的目标Java版本 。如果没有正确配置插件,可能会导致以下问题:1. Java版本不匹配;2.字节码版本不匹配。
如果不用插件的话,就必须确保Spring的版本和JDK的版本匹配才行,即对Spring框架版本和JDK版本进行调整
不匹配时所报的错误:
步骤4:创建配置类
java
@Configuration
@ComponentScan("com.baidu.controller")
public class SpringMvcConfig {
}
步骤5:创建Controller类
java
@Controller
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "{'info':'springmvc'}";
}
}
return "{'info':'springmvc'}"
:前面我们说过现在主要的是前端发送异步请求,后台响应json数据,如果这里没有返回值,会报错
@ResponseBody
:设置当前控制器方法响应内容为当前返回值,无需解析。如果方法直接返回字符串,springmvc会把字符串当成页面的名称在项目中进行查找返回,会报404错误,找不到资源。
步骤6:使用配置类替换web.xml
将web.xml删除,换成ServletContainersInitConfig
java
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//加载springmvc配置类
protected WebApplicationContext createServletApplicationContext() {
//初始化WebApplicationContext对象
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
//加载指定配置类
ctx.register(SpringMvcConfig.class);
return ctx;
}
//设置由springmvc控制器处理的请求映射路径
protected String[] getServletMappings() {
return new String[]{"/"};
}
//加载spring配置类
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
-
AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类
-
AbstractDispatcherServletInitializer提供了三个接口方法供用户实现
-
createServletApplicationContext方法 ,创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围
-
getServletMappings方法,设定SpringMVC对应的请求映射路径,即SpringMVC拦截哪些请求
注意:
DispatcherServlet是Spring MVC框架的核心组件,负责处理所有的HTTP请求并将它们分发到相应的控制器(Controller)进行处理。
return new String[]{"/"};
这种配置方式意味着所有的请求都会经过DispatcherServlet,然后由该框架的其他组件来处理和响应。DispatcherServlet拦截请求并不意味着请求被阻止或拒绝访问,而是表示DispatcherServlet会接管这个请求并尝试将其分发到合适的Controller进行处理。
如何取消部分拦截?
如果希望某些请求不被拦截(特别是某些静态资源),需要配置DispatcherServlet以排除这些路径
添加一个配置类
java@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { //设置静态资源访问过滤,当前类需要设置为配置类,并被扫描加载 @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { //当访问/pages/????时候,从/pages目录下查找内容 registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } }
配置好之后要让SpringMvcConfig 扫描到这个包(@ComponentScan注解)
-
createRootApplicationContext方法,如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式和createServletApplicationContext相同。
这类初始化器(initializer)负责配置和初始化
DispatcherServlet
,这是 Spring MVC 的中央控制器Spring MVC 应用程序通常会包含两个上下文:(仍有疑点,不能区分二者的本质)
- Servlet 上下文(Servlet WebApplicationContext): 用于配置
DispatcherServlet
。在你的代码中,这个上下文通过createServletApplicationContext()
方法创建,并加载了指定的配置类SpringMvcConfig.class
。 - 根应用程序上下文(Root WebApplicationContext): 用于配置应用程序级别的共享的 bean,比如 Service 层、Repository 层等。这个上下文通常被整个应用程序共享,而不是针对某个特定的 Servlet。在你的代码中,
createRootApplicationContext()
方法返回null
,表示在这里并没有定义根应用程序上下文。
- Servlet 上下文(Servlet WebApplicationContext): 用于配置
-
createServletApplicationContext用来加载SpringMVC环境
-
createRootApplicationContext用来加载Spring环境
-
为什么这上面没有注解配置?(@XXX)
这个类是作为
AbstractDispatcherServletInitializer
的子类,而这个类(AbstractDispatcherServletInitializer )本身就是Spring框架提供的用于配置DispatcherServlet初始化的一部分。Spring框架会自动检测并使用这个类 ,所以并不需要显式地添加其他配置注解。
步骤7:配置Tomcat环境
步骤8:启动运行项目
步骤9:浏览器访问
运行结果:
知识点1:@Controller
名称 | @Controller |
---|---|
类型 | 类注解 |
位置 | SpringMVC控制器类定义上方 |
作用 | 设定SpringMVC的核心控制器bean |
知识点2:@RequestMapping
名称 | @RequestMapping |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法请求访问路径 |
相关属性 | value(默认),请求访问路径 |
知识点3:@ResponseBody
名称 | @ResponseBody |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法响应内容为当前返回值,无需解析 |
2.3 案例总结
2.3.1 工作类型
- 一次性工作
- 创建工程,设置服务器,加载工程
- 导入坐标
- 创建web容器启动类,加载SpringMVC配置,并设置SpringMVC请求拦截路径
- SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器bean)
- 多次工作
- 定义处理请求的控制器类
- 定义处理请求的控制器方法,并配置映射路径(@RequestMapping)与返回json数据(@ResponseBody)
2.3.2 工作流程解析
SpringMVC的使用过程总共分两个阶段来分析,分别是启动服务器初始化过程和单次请求过程
2.3.2.1 启动服务器初始化过程
-
服务器启动,执行ServletContainersInitConfig类 ,初始化web容器,功能类似于以前的web.xml
-
执行createServletApplicationContext方法,创建了WebApplicationContext对象
该方法加载SpringMVC的配置类SpringMvcConfig来初始化SpringMVC的容器
-
加载SpringMvcConfig配置类
-
执行@ComponentScan 加载对应的bean
扫描指定包及其子包下所有类上的注解,如Controller类上的@Controller注解
-
加载UserController ,每个@RequestMapping的名称对应一个具体的方法
- 此时就建立了
/save
和 save方法的对应关系
- 此时就建立了
-
执行getServletMappings方法,设定SpringMVC拦截请求的路径规则
/
代表所拦截请求的路径规则,只有被拦截后才能交给SpringMVC来处理请求
2.3.2.2 单次请求过程
- 发送请求
http://localhost/save
- web容器发现该请求满足SpringMVC拦截规则,将请求交给SpringMVC处理
- 解析请求路径/save
- 由/save匹配执行对应的方法save()
- 上面的第5.步已经将请求路径和方法建立了对应关系,通过/save就能找到对应的save方法
- 执行save()
- 检测到有@ResponseBody直接将save()方法的返回值作为响应体返回给请求方
2.4 bean加载控制
2.4.1 问题分析
SpringMvcConfig配置类和之前的SpringConfig配置类
这两个配置类都需要加载资源,那么它们分别都需要加载哪些内容?
分析项目结构:
- config目录存入的是配置类 ,写过的配置类有:
- ServletContainersInitConfig
- SpringConfig
- SpringMvcConfig
- JdbcConfig
- MybatisConfig
- controller目录存放的是SpringMVC的controller类
- service目录存放的是service接口和实现类
- dao目录存放的是dao/Mapper接口
controller、service和dao这些类都需要被容器管理成bean对象,
那么到底是该让SpringMVC加载还是让Spring加载呢?
- SpringMVC加载的bean:
- 表现层bean(也就是controller包下的类)
- Spring加载的bean:
- 业务bean(Service)
- 功能bean(DataSource,SqlSessionFactoryBean,MapperScannerConfigurer等)
如何让Spring和SpringMVC分开加载各自的内容?
在SpringMVC的配置类SpringMvcConfig
中使用注解@ComponentScan
,我们只需要将其扫描范围设置到controller即可,如
在Spring的配置类SpringConfig
中使用注解@ComponentScan
,当时扫描的范围中其实是已经包含了controller,如:
从包结构来看的话,Spring已经多把SpringMVC的controller类也给扫描到,所以针对这个问题该如何解决?
因为功能不同,如何避免Spring错误加载到SpringMVC的bean?
2.4.2 设置bean加载控制
- 方式一:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等
- 方式二:Spring加载的bean设定扫描范围为com.baidu,排除掉controller包中的bean
- 方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中[了解即可]
方式一: 修改Spring配置类,设定扫描范围为精准范围。
java
@Configuration
@ComponentScan({"com.baidu.service","com.baidu.dao"})
public class SpringConfig {
}
说明:
上述只是通过例子说明可以精确指定让Spring扫描对应的包结构,真正在做开发的时候,因为Dao最终是交给MapperScannerConfigurer
对象来进行扫描处理的,我们只需要将其扫描到service包即可。
方式二: 修改Spring配置类,设定扫描范围为com.baidu,排除掉controller包中的bean
java
@Configuration
@ComponentScan(value="com.baidu",
excludeFilters=@ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {
}
-
excludeFilters属性:设置扫描加载bean时,排除的过滤规则
-
type属性:设置排除规则,当前使用按照bean定义时的注解类型进行排除
- ANNOTATION:按照注解排除
- ASSIGNABLE_TYPE:按照指定的类型过滤
- ASPECTJ:按照Aspectj表达式排除,基本上不会用
- REGEX:按照正则表达式排除
- CUSTOM:按照自定义规则排除
只需要知道第一种ANNOTATION即可
-
classes属性:设置排除的具体注解类,当前设置排除@Controller定义的bean
如何测试controller类已经被排除掉了?
java
public class App{
public static void main (String[] args){
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
System.out.println(ctx.getBean(UserController.class));
}
}
如果被排除了,该方法执行就会报bean未被定义的错误
注意:
- 需要把SpringMvcConfig配置类上的
@ComponentScan
注解注释掉,否则不会报错 @Import({JdbcConfig.class,MybatisConfig.class})
这类的注解也要注释掉,否则不会报错
原因是:
- Spring配置类扫描的包是
com.baidu
- SpringMVC的配置类,
SpringMvcConfig
上有一个@Configuration注解,也会被Spring扫描到 - SpringMvcConfig上又有一个@ComponentScan,把controller类又给扫描进来了
- 所以如果不把@ComponentScan注释掉,Spring配置类将Controller排除,但是因为扫描到SpringMVC的配置类,又将其加载回来,演示的效果就出不来
- 解决方案,也简单,把SpringMVC的配置类移出Spring配置类的扫描范围即可。
方式三:要想在tomcat服务器启动将其加载
有了Spring的配置类,要想在tomcat服务器启动将其加载,我们需要修改ServletContainersInitConfig
Servlet上下文:SpringMvcConfig
根应用程序上下文:SpringConfig
java
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringConfig.class);
return ctx;
}
}
对于上述的配置方式,Spring还提供了一种更简单的配置方式,可以不用再去创建AnnotationConfigWebApplicationContext
对象,不用手动register
对应的配置类:
java
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
如果没有对应的SpringConfig或者SpringMvcConfig,返回null或者new Class[0](本质上也是null)
知识点1:@ComponentScan
名称 | @ComponentScan |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置spring配置类扫描路径,用于加载使用注解格式定义的bean |
相关属性 | excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)和具体项(classes) includeFilters:加载指定的bean,需要指定类别(type)和具体项(classes) |
3 PostMan工具的使用
PostMan是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件
作用:常用于进行接口测试
3.1 下载与安装
官网下载:Postman下载官网
3.2 请求与响应
SpringMVC是web层的框架,主要的作用是接收请求、接收数据、响应结果
3.2.1 设置请求映射路径
多个contoller中都有一个方法,如何设置映射路径?
-
当类上和方法上都添加了
@RequestMapping
注解,前端发送请求的时候,要和两个注解的value值相加匹配才能访问到。 -
@RequestMapping注解value属性前面加不加
/
都可以javapackage com.baidu.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller //类上方配置的请求映射与方法上面配置的请求映射连接在一起,形成完整的请求映射路径 @RequestMapping("/user") public class UserController { //请求路径映射 @RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'module':'user save'}"; } //请求路径映射 @RequestMapping("/delete") @ResponseBody public String delete(){ System.out.println("user delete ..."); return "{'module':'user delete'}"; } }
3.2.2 请求参数
针对于不同的请求(GET,POST)前端如何发送,后端如何接收?
3.2.2.1 GET
GET发送单个参数
略
GET发送多个参数
报错:Failed to register servlet with name 'dispatcher'. Check if there is another servlet registered under the same name.
原因:target下面出现了两个相同的配置类,删去其中一个就好了
注意:Tomcat启动后访问Spring MVC应用时,Tomcat会运行target目录下的编译后的类文件。
java
@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'}";
}
}
url
http://localhost:8080/springmvc_04_request_param/commonParam?name=baidu&age=15
3.2.2.2 GET乱码
GET请求中文乱码
乱码的原因:Tomcat8.5以后的版本已经处理了中文乱码的问题,但是IDEA中的Tomcat插件目前只到Tomcat7;
所以需要修改pom.xml来解决GET请求中文乱码问题
xml
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<port>8080</port><!--tomcat端口号-->
<path>/</path> <!--虚拟目录-->
<uriEncoding>UTF-8</uriEncoding><!--访问路径编解码字符集-->
</configuration>
</plugin>
</plugins>
</build>
3.2.2.3 POST
POST发送参数
POST发送表单数据
**注意:要选择x-www-form-urlencoded,而不是form-date,选择后者会报错**报错如下:
Request processing failed; nested exception is java.lang.IllegalStateException: Optional int parameter 'age' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type.
后端和GET一致,不需要做任何修改
java
@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'}";
}
}
3.2.2.4 POST乱码
POST请求中文乱码
解决办法:配置过滤器(即替代web.xml的那个类),重写其getServletFilters方法
CharacterEncodingFilter是在spring-web包中,所以用之前需要导入对应的jar包
java
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};
}
}
3.2.3 五种类型参数传递
- 普通参数
- POJO类型参数
- 嵌套POJO类型参数
- 数组类型参数
- 集合类型参数
3.2.3.1 普通参数
不使用参数注解接受的前提:对应的值拼写一定要相同
如果不相同,需要使用参数注解:@RequestParam,否则接受为null
3.2.3.2 POJO类型参数
- POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数
POJO类:
java
public class User {
private String name;
private int age;
//setter...getter...略
}
发送与接受:
注意:
-
POJO参数接收,前端GET和POST发送请求数据的方式不变。
-
请求参数key的名称要和POJO中属性的名称一致,否则无法封装。
-
没有的属性可以不用填写,后端接受直接为null
-
参数的顺序可以不用一一对应,下面的请求同样能达到效果
http://localhost:8080/springmvc_04_request_param/pojoContainPojoParam?age=15&name=百度
3.2.3.3 嵌套POJO类型参数
如果POJO对象中嵌套了其他的POJO类,如:
java
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属性参数
发送与接受:
url
http://localhost:8080/springmvc_04_request_param/pojoContainPojoParam?name=百度&age=15&address.city=sichuan&address.province=chengdu
3.2.3.4 数组类型参数
- 数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数
注意:参数名必须一致才能封装到一个数组中
发送与接受:
http://localhost:8080/springmvc_04_request_param/arrayParam?likes=movie&likes=music&likes=dance
3.2.3.5 集合类型参数
如List集合
如果:
http://localhost:8080/springmvc_04_request_param/listParam?likes=movie&likes=music&likes=dance
对应的后端:
会报错:SpringMVC将List看做是一个POJO对象 来处理,将其创建一个对象并准备把前端的数据封装到对象中 ,但是List是一个接口无法创建对象,所以报错。
发送与接受:
使用参数注解:@RequestParam,同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
- 集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系
- 对于简单数据类型使用数组会比集合更简单些。
3.2.4 JSON数据传输参数
前端如果发送的是JSON数据,后端该如何接收?
对于JSON数据类型,我们常见的有三种:
- json普通数组(["value1","value2","value3",...])
- json对象({key1:value1,key2:value2,...})
- json对象数组([{key1:value1,...},{key2:value2,...}])
SpringMVC默认使用的是jackson来处理json的转换,所以需要在pom.xml添加jackson依赖
xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
3.2.4.1 JSON普通数组
用List集合接受
-
开启SpringMVC注解支持
在SpringMVC的配置类中开启SpringMVC的注解支持,这里面就包含了将JSON转换成对象的功能。
@EnableWebMvc是Spring Framework中的注解,用于启用Spring MVC的基本功能。当你在配置类上使用
@EnableWebMvc
注解时,它会开启Spring MVC的特性,包括但不限于:- 启用
@Controller
注解 - 启用
@RequestMapping
注解 - 启用
@ExceptionHandler
注解 - 启用
@ResponseBody
注解 - 启用视图解析器:于解析Controller方法返回的视图名
@EnableWebMvc是较旧的配置Spring MVC的方式 。如果使用SpringBoot,它提供了自动配置,通常不需要显式使用@EnableWebMvc,因为它已经包含在默认配置中。
java@Configuration @ComponentScan("com.baidu.controller") //开启json数据类型自动转换 @EnableWebMvc public class SpringMvcConfig { }
- 启用
-
参数前添加@RequestBody
使用**@RequestBody注解将外部传递的 json数组数据映射到形参的集合对象中作为数据**
java//使用@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'}"; }
-
启动运行程序
-
PostMan发送JSON数据接收响应
3.2.4.2 JSON对象
用对象接受
-
数据:
json{ "name":"itcast", "age":15 }
-
后端接收数据:
java@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也有数据,我们需求修改前端传递的数据内容:
json{ "name":"itcast", "age":15, "address":{ "province":"beijing", "city":"beijing" } }
3.2.4.3 JSON对象数组
集合中保存多个POJO的情况
用List集合接受
-
数据
json[ {"name":"itcast","age":15}, {"name":"baidu","age":12} ]
-
后端接收数据:
java@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'}"; }
-
发送请求和接受数据
以上这些请求用POST也可以实现
3.2.4.4 小结
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接收请求参数
3.2.5 日期类型参数传递
难点之一,形式多样,比如:
- 2088-08-18
- 2088/08/18
- 08/18/2088
- ...
步骤1:编写方法接收日期数据
在UserController类中添加方法,把**参数设置为日期类型**
java
@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:查看控制台
步骤5:更换日期格式
我们在方法中多添加一个日期参数(甚至可以携带时间),相应的接受参数要设置相应的接受格式
java
@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发送请求,携带多个不同的日期格式,格式的设置可以利用@DateTimeFormat
,
http://localhost:8080/springmvc_04_request_param/dataParam?date=2018/08/08&date1=2019-09-09&date3=2020/02/20 20:20:20
知识点1:@DateTimeFormat
名称 | @DateTimeFormat |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参前面 |
作用 | 设定日期时间型数据格式 |
相关属性 | pattern:指定日期时间格式字符串 |
内部实现原理
讲解内部原理之前,我们需要先思考个问题:
- 前端传递日期,后端使用日期Date接收
- 前端传递JSON数据,后端使用对象接收
- 前端传递字符串,后端使用String接收
- 后台需要的数据类型有很多种
- 在数据的传递过程中存在很多类型的转换
问:谁来做这个类型转换?
答:SpringMVC
问:SpringMVC是如何实现类型转换的?
答:SpringMVC中提供了很多类型转换接口和实现类
在框架中,有一些类型转换接口,其中有:
-
Converter接口
java/** * 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接口的实现类
Ctrl+D 搜索类
Ctrl+H 显示类结构图
框架中有提供很多对应Converter接口的实现类,用来实现不同数据类型之间的转换,如:
请求参数年龄数据(String→Integer)
日期格式转换(String → Date)
-
HttpMessageConverter接口
该接口是实现对象与JSON之间的转换工作
注意:SpringMVC的配置类把@EnableWebMvc当做标配配置上去,不要省略
3.2.6 响应
4 Rest风格
4.1 REST简介
REST(Representational State Transfer),表现形式状态转换,它是一种软件架构风格
当我们想表示一个网络资源的时候,可以使用两种方式:
- 传统风格资源描述形式
http://localhost/user/getById?id=1
查询id为1的用户信息http://localhost/user/saveUser
保存用户信息- REST风格描述形式
http://localhost/user/1
http://localhost/user
- 特点:
- 传统:不仅麻烦,也不安全,因为会程序的人读取了你的请求url地址,就大概知道该url实现的是一个什么样的操作
- REST:请求地址变的简单了,并且光看请求URL并不是很能猜出来该URL的具体功能
REST的优点:
- 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
- 书写简化
如何区分:
一个相同的url地址即可以是新增也可以是修改或者查询,那么到底我们该如何区分该请求到底是什么操作呢?
按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
按照不同的请求方式代表不同的操作类型
-
发送GET请求是用来做查询
-
发送POST请求是用来做新增
-
发送PUT请求是用来做修改
-
发送DELETE请求是用来做删除
-
http://localhost/users
查询全部用户信息 GET(查询) -
http://localhost/users/1
查询指定用户信息 GET(查询) -
http://localhost/users
添加用户信息 POST(新增/保存) -
http://localhost/users
修改用户信息 PUT(修改/更新) -
http://localhost/users/1
删除用户信息 DELETE(删除)
请求的方式比较多,但是比较常用的就4种,分别是GET,POST,PUT,DELETE。
注意:
-
上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范
- REST中规定GET/POST/PUT/DELETE针对的是查询/新增/修改/删除,但是我们如果非要用GET请求做删除,这点在程序上运行是可以实现的,但是如果绝大多数人都遵循这种风格。
- REST提供了对应的架构方式,按照这种架构设计项目可以降低开发的复杂性,提高系统的可伸缩性
-
描述模块的名称通常使用复数 ,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts...
-
根据REST风格对资源进行访问称为RESTful。
4.2 RESTful入门
4.2.1 行为动作的确定
在controller中,设置当前请求方法为GET/POST/PUT/DELETE,表示REST风格中的某种操作
例如删除:
java
@Controller
public class UserController {
//设置当前请求方法为DELETE,表示REST风格中的删除操作
@RequestMapping(value = "/users",method = RequestMethod.DELETE)
@ResponseBody
public String delete(Integer id) {
System.out.println("user delete..." + id);
return "{'module':'user delete'}";
}
}
如果想访问这个方法但是发送的不是DELETE请求,会报错
4.2.2 携带参数
- 前端发送请求的时候直接添加要传递的参数
- 修改@RequestMapping的value属性,并在方法的形参前添加@PathVariable注解,确保路径匹配,并且能够接收参数
例:删除用户,传递单个参数
http://localhost/users/1
java
@Controller
public class UserController {
//设置当前请求方法为DELETE,表示REST风格中的删除操作
@RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id) {
System.out.println("user delete..." + id);
return "{'module':'user delete'}";
}
}
参数匹配规则:
例:删除用户,传递多个参数
http://localhost/users/1/tom
java
@Controller
public class UserController {
//设置当前请求方法为DELETE,表示REST风格中的删除操作
//@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
@RequestMapping(value = "/users/{id}/{username}",method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id,@PathVariable String username){
System.out.println("user delete..." + id+" "+username);
return "{'module':'user delete'}";
}
}
或者
java
@Controller
public class UserController {
//设置当前请求方法为PUT,表示REST风格中的修改操作
@RequestMapping(value = "/users",method = RequestMethod.PUT)
@ResponseBody
public String update(@RequestBody User user) {
System.out.println("user update..." + user);
return "{'module':'user update'}";
}
}
例:查询用户,不传入参数
java
@Controller
public class UserController {
//设置当前请求方法为GET,表示REST风格中的查询操作
@RequestMapping(value = "/users" ,method = RequestMethod.GET)
@ResponseBody
public String getAll() {
System.out.println("user getAll...");
return "{'module':'user getAll'}";
}
}
4.2.3 小结
RESTful入门案例,我们需要学习的内容如下:
(1)设定Http请求动作(动词)
java
@RequestMapping(value="",method = RequestMethod.POST|GET|PUT|DELETE)
(2)设定请求参数(路径变量)
java
@RequestMapping(value="/users/{id}",method = RequestMethod.DELETE)
@ReponseBody
public String delete(@PathVariable Integer id){
}
知识点1:@PathVariable
名称 | @PathVariable |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应 |
4.2.4 接受参数的注解
关于接收参数,我们学过三个注解@RequestBody
、@RequestParam
、@PathVariable
,这三个注解之间的区别和应用分别是什么?
- 区别
- @RequestParam用于接收url地址传参或表单传参
- @RequestBody用于接收json数据
- @PathVariable用于接收路径参数,使用{参数名称}描述路径参数
- 应用
-
后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
-
如果发送非json格式数据,选用@RequestParam接收请求参数
为GET,表示REST风格中的查询操作
@RequestMapping(value = "/users" ,method = RequestMethod.GET)
@ResponseBody
public String getAll() {
System.out.println("user getAll...");
return "{'module':'user getAll'}";
}
}[外链图片转存中...(img-hGW5qLbj-1705223006403)]
[外链图片转存中...(img-SWln2QCw-1705223006403)]
4.2.3 小结
RESTful入门案例,我们需要学习的内容如下:
(1)设定Http请求动作(动词)
java@RequestMapping(value="",method = RequestMethod.POST|GET|PUT|DELETE)
-
(2)设定请求参数(路径变量)
java
@RequestMapping(value="/users/{id}",method = RequestMethod.DELETE)
@ReponseBody
public String delete(@PathVariable Integer id){
}
知识点1:@PathVariable
名称 | @PathVariable |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应 |
4.2.4 接受参数的注解
关于接收参数,我们学过三个注解@RequestBody
、@RequestParam
、@PathVariable
,这三个注解之间的区别和应用分别是什么?
- 区别
- @RequestParam用于接收url地址传参或表单传参
- @RequestBody用于接收json数据
- @PathVariable用于接收路径参数,使用{参数名称}描述路径参数
- 应用
- 后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
- 如果发送非json格式数据,选用@RequestParam接收请求参数
- 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值