现在web程序大都基于三层架构来实现
浏览器发送一个请求给后端服务器,后端服务器现在是使用Servlet来接收请求和数据
如果所有的处理都交给Servlet来处理的话,所有的东西都耦合在一起,对后期的维护和扩展极为不利
将后端服务器Servlet拆分成三层,分别是web、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视图。
综上:
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
优点
因为SpringMVC是一个Web框架,需要我们提供Servlet(将来是要替换Servlet)
用Servlet是如何进行开发的
回顾:用Servlet是如何进行开发的
创建web工程(Maven结构)
设置tomcat服务器,加载web工程(tomcat插件)
导入坐标(Servlet)
定义处理请求的功能类(UserServlet)
设置请求映射(配置映射关系)
SpringMVC的制作过程和上述流程几乎是一致的,具体的实现流程:
创建web工程(Maven结构)
设置tomcat服务器,加载web工程(tomcat插件)
导入坐标(SpringMVC+Servlet)
定义处理请求的功能类(UserController)
设置请求映射(配置映射关系)
将SpringMVC设定加载到Tomcat容器中
步骤1: 创建Maven项目
打开IDEA,创建一个新的web项目
① 使用骨架(archetype):
maven项目的三个坐标,可采用默认,也可自己修改
点击创建后,会自动从阿里云下载包:
下载完成:
如果发现创建的模块位置不对,可以在refactor里面移动:
修改路径:(在这里删掉后面即可)
如果消失了,通过maven导入的方式导入即可:
进行一些修改与添加:
1 在依赖中选择打包方式:war
2 默认没有java和resources目录,需要手动完成创建补齐,最终的目录结果如下
② 不使用骨架:
剩下的就是修改打包方式和补全相关目录了
步骤2:导入jar包
将pom.xml中多余的内容删除掉,再添加SpringMVC需要的依赖
<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.0modelVersion>
<groupId>com.baidugroupId>
<artifactId>springmvc_01_quickstartartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<dependencies>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.10.RELEASEversion>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.1version>
<configuration>
<port>8080port>
<path>/path>
configuration>
plugin>
plugins>
build>
project>
回顾: servlet的坐标为什么需要添加
?
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:创建配置类
@Configuration
@ComponentScan("com.baidu.controller")
public class SpringMvcConfig {
}
步骤5:创建Controller类
@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
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以排除这些路径
添加一个配置类
@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
,表示在这里并没有定义根应用程序上下文。
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控制器类或方法定义上方 |
作用 | 设置当前控制器方法响应内容为当前返回值,无需解析 |
SpringMVC的使用过程总共分两个阶段来分析,分别是启动服务器初始化过程和单次请求过程
服务器启动,执行ServletContainersInitConfig类,初始化web容器,功能类似于以前的web.xml
执行createServletApplicationContext方法,创建了WebApplicationContext对象
该方法加载SpringMVC的配置类SpringMvcConfig来初始化SpringMVC的容器
加载SpringMvcConfig配置类
执行@ComponentScan加载对应的bean
扫描指定包及其子包下所有类上的注解,如Controller类上的@Controller注解
加载UserController,每个@RequestMapping的名称对应一个具体的方法
/save
和 save方法的对应关系执行getServletMappings方法,设定SpringMVC拦截请求的路径规则
/
代表所拦截请求的路径规则,只有被拦截后才能交给SpringMVC来处理请求http://localhost/save
SpringMvcConfig配置类和之前的SpringConfig配置类
这两个配置类都需要加载资源,那么它们分别都需要加载哪些内容?
分析项目结构:
controller、service和dao这些类都需要被容器管理成bean对象,
那么到底是该让SpringMVC加载还是让Spring加载呢?
如何让Spring和SpringMVC分开加载各自的内容?
在SpringMVC的配置类SpringMvcConfig
中使用注解@ComponentScan
,我们只需要将其扫描范围设置到controller即可,如
在Spring的配置类SpringConfig
中使用注解@ComponentScan
,当时扫描的范围中其实是已经包含了controller,如:
从包结构来看的话,Spring已经多把SpringMVC的controller类也给扫描到,所以针对这个问题该如何解决?
因为功能不同,如何避免Spring错误加载到SpringMVC的bean?
方式一: 修改Spring配置类,设定扫描范围为精准范围。
@Configuration
@ComponentScan({"com.baidu.service","com.baidu.dao"})
public class SpringConfig {
}
说明:
上述只是通过例子说明可以精确指定让Spring扫描对应的包结构,真正在做开发的时候,因为Dao最终是交给MapperScannerConfigurer
对象来进行扫描处理的,我们只需要将其扫描到service包即可。
方式二: 修改Spring配置类,设定扫描范围为com.baidu,排除掉controller包中的bean
@Configuration
@ComponentScan(value="com.baidu",
excludeFilters=@ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {
}
excludeFilters属性:设置扫描加载bean时,排除的过滤规则
type属性:设置排除规则,当前使用按照bean定义时的注解类型进行排除
只需要知道第一种ANNOTATION即可
classes属性:设置排除的具体注解类,当前设置排除@Controller定义的bean
如何测试controller类已经被排除掉了?
public class App{
public static void main (String[] args){
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
System.out.println(ctx.getBean(UserController.class));
}
}
如果被排除了,该方法执行就会报bean未被定义的错误
注意:
@ComponentScan
注解注释掉,否则不会报错@Import({JdbcConfig.class,MybatisConfig.class})
这类的注解也要注释掉,否则不会报错原因是:
com.baidu
SpringMvcConfig
上有一个@Configuration注解,也会被Spring扫描到方式三:要想在tomcat服务器启动将其加载
有了Spring的配置类,要想在tomcat服务器启动将其加载,我们需要修改ServletContainersInitConfig
Servlet上下文:SpringMvcConfig
根应用程序上下文:SpringConfig
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
对应的配置类:
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) |
PostMan是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件
作用:常用于进行接口测试
官网下载:Postman下载官网
SpringMVC是web层的框架,主要的作用是接收请求、接收数据、响应结果
多个contoller中都有一个方法,如何设置映射路径?
当类上和方法上都添加了@RequestMapping
注解,前端发送请求的时候,要和两个注解的value值相加匹配才能访问到。
@RequestMapping注解value属性前面加不加/
都可以
package 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'}";
}
}
针对于不同的请求(GET,POST)前端如何发送,后端如何接收?
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目录下的编译后的类文件。
@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'}";
}
}
http://localhost:8080/springmvc_04_request_param/commonParam?name=baidu&age=15
GET请求中文乱码
乱码的原因:Tomcat8.5以后的版本已经处理了中文乱码的问题,但是IDEA中的Tomcat插件目前只到Tomcat7;
所以需要修改pom.xml来解决GET请求中文乱码问题
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.mavengroupId>
<artifactId>tomcat7-maven-pluginartifactId>
<version>2.1version>
<configuration>
<port>8080port>
<path>/path>
<uriEncoding>UTF-8uriEncoding>
configuration>
plugin>
plugins>
build>
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一致,不需要做任何修改
@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'}";
}
}
POST请求中文乱码
解决办法:配置过滤器(即替代web.xml的那个类),重写其getServletFilters方法
CharacterEncodingFilter是在spring-web包中,所以用之前需要导入对应的jar包
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};
}
}
不使用参数注解接受的前提:对应的值拼写一定要相同
如果不相同,需要使用参数注解:@RequestParam,否则接受为null
POJO类:
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=百度
如果POJO对象中嵌套了其他的POJO类,如:
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属性参数
发送与接受:
http://localhost:8080/springmvc_04_request_param/pojoContainPojoParam?name=百度&age=15&address.city=sichuan&address.province=chengdu
注意:参数名必须一致才能封装到一个数组中
发送与接受:
http://localhost:8080/springmvc_04_request_param/arrayParam?likes=movie&likes=music&likes=dance
如List集合
如果:
http://localhost:8080/springmvc_04_request_param/listParam?likes=movie&likes=music&likes=dance
对应的后端:
会报错:SpringMVC将List看做是一个POJO对象来处理,将其创建一个对象并准备把前端的数据封装到对象中,但是List是一个接口无法创建对象,所以报错。
发送与接受:
使用参数注解:@RequestParam,同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
前端如果发送的是JSON数据,后端该如何接收?
对于JSON数据类型,我们常见的有三种:
SpringMVC默认使用的是jackson来处理json的转换,所以需要在pom.xml添加jackson依赖
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.9.0version>
dependency>
用List集合接受
开启SpringMVC注解支持
在SpringMVC的配置类中开启SpringMVC的注解支持,这里面就包含了将JSON转换成对象的功能。
@EnableWebMvc是Spring Framework中的注解,用于启用Spring MVC的基本功能。当你在配置类上使用
@EnableWebMvc
注解时,它会开启Spring MVC的特性,包括但不限于:
- 启用
@Controller
注解- 启用
@RequestMapping
注解- 启用
@ExceptionHandler
注解- 启用
@ResponseBody
注解- 启用视图解析器:于解析Controller方法返回的视图名
@EnableWebMvc是较旧的配置Spring MVC的方式。如果使用SpringBoot,它提供了自动配置,通常不需要显式使用@EnableWebMvc,因为它已经包含在默认配置中。
@Configuration
@ComponentScan("com.baidu.controller")
//开启json数据类型自动转换
@EnableWebMvc
public class SpringMvcConfig {
}
参数前添加@RequestBody
使用**@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据**
//使用@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数据接收响应
用对象接受
数据:
{
"name":"itcast",
"age":15
}
后端接收数据:
@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也有数据,我们需求修改前端传递的数据内容:
{
"name":"itcast",
"age":15,
"address":{
"province":"beijing",
"city":"beijing"
}
}
集合中保存多个POJO的情况
用List集合接受
数据
[
{"name":"itcast","age":15},
{"name":"baidu","age":12}
]
后端接收数据:
@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也可以实现
SpringMVC接收JSON数据的实现步骤为:
(1)导入jackson包
(2)使用PostMan发送JSON数据
(3)开启SpringMVC注解驱动,在配置类上添加@EnableWebMvc注解
(4)Controller方法的参数前添加@RequestBody注解
知识点1:@EnableWebMvc
名称 | @EnableWebMvc |
---|---|
类型 | 配置类注解 |
位置 | SpringMVC配置类定义上方 |
作用 | 开启SpringMVC多项辅助功能 |
知识点2:@RequestBody
名称 | @RequestBody |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次 |
@RequestBody与@RequestParam区别
区别
应用
难点之一,形式多样,比如:
在UserController类中添加方法,把**参数设置为日期类型**
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date)
System.out.println("参数传递 date ==> "+date);
return "{'module':'data param'}";
}
使用PostMan发送**GET请求,并设置date参数**
http://localhost/dataParam?date=2088/08/08
我们在方法中多添加一个日期参数(甚至可以携带时间),相应的接受参数要设置相应的接受格式
@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:指定日期时间格式字符串 |
内部实现原理
讲解内部原理之前,我们需要先思考个问题:
问:谁来做这个类型转换?
答:SpringMVC
问:SpringMVC是如何实现类型转换的?
答:SpringMVC中提供了很多类型转换接口和实现类
在框架中,有一些类型转换接口,其中有:
Converter接口
/**
* 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当做标配配置上去,不要省略
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规范
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts…
根据REST风格对资源进行访问称为RESTful。
在controller中,设置当前请求方法为GET/POST/PUT/DELETE,表示REST风格中的某种操作
例如删除:
@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请求,会报错
例:删除用户,传递单个参数
http://localhost/users/1
@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
@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'}";
}
}
或者
@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'}";
}
}
例:查询用户,不传入参数
@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'}";
}
}
RESTful入门案例,我们需要学习的内容如下:
(1)设定Http请求动作(动词)
@RequestMapping(value="",method = RequestMethod.POST|GET|PUT|DELETE)
(2)设定请求参数(路径变量)
@RequestMapping(value="/users/{id}",method = RequestMethod.DELETE)
@ReponseBody
public String delete(@PathVariable Integer id){
}
知识点1:@PathVariable
名称 | @PathVariable |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应 |
关于接收参数,我们学过三个注解@RequestBody
、@RequestParam
、@PathVariable
,这三个注解之间的区别和应用分别是什么?
[外链图片转存中...(img-hGW5qLbj-1705223006403)]
[外链图片转存中...(img-SWln2QCw-1705223006403)]
#### 4.2.3 小结
RESTful入门案例,我们需要学习的内容如下:
(1)设定Http请求动作(动词)
```java
@RequestMapping(value="",method = RequestMethod.POST|GET|PUT|DELETE)
(2)设定请求参数(路径变量)
@RequestMapping(value="/users/{id}",method = RequestMethod.DELETE)
@ReponseBody
public String delete(@PathVariable Integer id){
}
知识点1:@PathVariable
名称 | @PathVariable |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应 |
关于接收参数,我们学过三个注解@RequestBody
、@RequestParam
、@PathVariable
,这三个注解之间的区别和应用分别是什么?