SpringMVC
SpringMvc入门
1.设置SpringMVC配置类
2.创建Controller类
3.配置ResultMapping注解
4.配置ResponseBody注解,不然的话默认return为页面
5.配置ServletWeb配置,替换web.xml,实现AbstractDispatcherServletInitializer接口
6.设置ServletWeb中的方法的返回值,以及拦截路径
注意事项
SpringMVC是基于Spring的,在pom.xml只导入了spring-webmvc jar包的原因是它会自动依赖spring相关坐标
AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类
AbstractDispatcherServletInitializer提供了三个接口方法供用户实现
createServletApplicationContext方法,创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用
范围为ServletContext范围,即整个web容器范围
getServletMappings方法,设定SpringMVC对应的请求映射路径,即SpringMVC拦截哪些请求
createRootApplicationContext方法,如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式和createServletApplicationContext相同。
createServletApplicationContext用来加载SpringMVC环境
createRootApplicationContext用来加载Spring
@Controller
名称 | @Controller |
---|---|
类型 | 类注解 |
位置 | SpringMVC控制器类定义上方 |
作用 | 设定SpringMVC的核心控制器bean |
@ResquestMapping
名称 | @RequestMapping |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法请求访问路径 |
相关属性 | value(默认),请求访问路径 |
@ResponseBody
名称 | @ResponseBody |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法响应内容为当前返回值,无需解析 |
一次性工作
创建工程,设置服务器,加载工程
导入坐标
创建web容器启动类,加载SpringMVC配置,并设置SpringMVC请求拦截路径
SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器bean)
多次工作
定义处理请求的控制器类
定义处理请求的控制器方法,并配置映射路径(@RequestMapping)与返回json数据(@ResponseBody)
服务器初始化流程
1.服务器启动,执行ServletContainersInitConfig类,初始化web容器
-
功能类似于以前的web.xml
2.执行createServletApplicationContext方法,创建了WebApplicationContext对象
-
该方法加载SpringMVC的配置类SpringMvcConfig来初始化SpringMVC的容器
3.加载SpringMvcConfig配置类
4.执行@ComponentScan加载对应的bean
-
扫描指定包其子包下所有类上的注解,如Controller类上的@Controller注解
5.加载UserController,每个@RequestMapping的名称对应一个具体的方法
-
此时就建立了 /save 和 save方法的对应关系
6.执行getServletMappings方法,设定SpringMVC拦截请求的路径规则
-
代表所拦截请求的路径规则,只有被拦截后才能交给SpringMVC来处理请求
单次请求过程
1.发送请求http://localhost/save
2.web容器发现该请求满足SpringMVC拦截规则,将请求交给SpringMVC处理
3.解析请求路径/save
4.由/save匹配执行对应的方法save()
-
上面的第五步已经将请求路径和方法建立了对应关系,通过/save就能找到对应的save方法
5.执行save()
6.检测到有@ResponseBody直接将save()方法的返回值作为响应体返回给请求方
bean加载控制
当前目录结构
-
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等
-
在SpringMVC的配置中,只需要添加注解 Configuration 和 CompoentScan 并将 scan 扫描范围设置在 controller层即可
但是在Spring配置中,Scan 范围为整个包,所以 Spring 也会将 SpringMVC 扫描进来,但是由于功能不同所以不需要加载SpringMVC的Bean
-
方式一:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等
-
方式二:Spring加载的bean设定扫描范围为com.itheima,排除掉controller包中的bean
-
方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中[了解即可]
方法一:
直接进行配置精确路径即可
@Configuration @ComponentScan({"com.itheima.service","comitheima.dao"}) public class SpringConfig { }
因为Dao最终是交给MapperScannerConfigurer对象来进行扫描处理的,我们只需要将其扫描到service包即可
方法二:
@Configuration @ComponentScan(value = "com.itheima", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class SpringConfig { }
-
excludeFilters属性:设置扫描加载bean时,排除的过滤规则
-
type属性:设置排除规则,当前使用按照bean定义时的注解类型进行排除
-
ANNOTATION:按照注解排除
-
ASSIGNABLE_TYPE:按照指定的类型过滤
-
ASPECTJ:按照Aspectj表达式排除,基本上不会用
-
REGEX:按照正则表达式排除
-
CUSTOM:按照自定义规则排除
-
-
classes属性:设置排除的具体注解类,当前设置排除@Controller定义的bean
在进行测试是否排除的时候,需要将 SpringMVC 中的 Scan 去掉,因为,Spring 会将 SpringMVC 的配置扫描进来,因此也会将 SpringMVC 的 Scan 扫描进来,会再次加载进 Controller
启动 tomcat 服务器的时候我们需要将 Spring 和 SpringMVC 的配置加载进 tomcat 的服务器中,因此我们需要修改 ServletContainersInitConfig 配置类中的参数
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { //加载springMVC容器配置 @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringMvcConfig.class); return ctx; } //设置哪些请求归SpringMVC处理 @Override protected String[] getServletMappings() { return new String[]{"/"}; // 所有请求 } //加载Spring容器配置 @Override protected WebApplicationContext createRootApplicationContext() { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(SpringConfig.class); return ctx; } }
对于上述的配置方式,Spring还提供了一种更简单的配置方式,可以不用再去创建 AnnotationConfigWebApplicationContex t对象,不用手动register对应的配置类,即继承 AbstractAnnotationConfigDispatcherServletInitializer 类
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class};// Spring } protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMvcConfig.class}; //SpringMve } protected String[] getServletMappings() { return new String[]{"/"}; } // 解决乱码问题 @Override protected Filter[] getServletFilters() { CharacterEncodingFilter filter = new CharacterEncodingFilter(); filter.setEncoding("UTF-8"); return new Filter[]{filter}; }
名称 | @ComponetScan |
---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置spring配置类扫描路径,用于加载使用注解格式定义的bean |
相关属 | excludeFilters:排除扫描路径中加载的bean,includeFilters:加载指定的bean,需要指定类别(type)和具体项(classes) |
请求与响应
-
请求映射路径
-
请求参数
-
日期类型参数传递
-
响应json数据
设置映射路径
如果只通过 ResultMapping 的映射路径来进行设置路径的话,当存在多个模块出现共同方法的时候就会出现找不到路径的问题,因此我们需要解决这种问题的出现
通过观察我们可以发现,我们可以根据模块的名称来设置相应的绝对路径,因此可以设置为这种格式
@RequestMapping("/user/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'module':'user save'}"; }
问题是解决了,但是每个方法前面都需要进行修改,写起来比较麻烦而且还有很多重复代码,如果/user后期发生变化,所有的方法都需要改,耦合度太高。因此我们可以将 RequeMapping 配置在 Controller 类的路径上
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/save") @ResponseBody public String save(){ System.out.println("user save ..."); return "{'module':'user save'}"; } }
-
当类上和方法上都添加了@RequestMapping注解,前端发送请求的时候,要和两个注解的value值相加匹配才能访问到。
-
@RequestMapping注解value属性前面加不加/都可以
请求参数
请求路径设置好后,只要确保页面发送请求地址和后台Controller类中配置的路径一致,就可以接收到前端的请求,接收到请求后,如何接收页面传递的参数?
对于常见的请求方式主要有两种,分别为
-
Post
-
Get
Get请求
接受单个普通参数:
@Controller public class UserController { @RequestMapping("/commonParam") @ResponseBody public String commonParam(String name) { System.out.println("普通参数传递 name ==> " + name); return "{'module':'commonParam'}"; } }
接受多个普通参数:
@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'}"; } }
Get请求中出现中文乱码:
只需要修改xml配置文件即可
<configuration> <port>80</port><!--tomcat端口号--> <path>/</path> <!--虚拟目录--> <uriEncoding>UTF-8</uriEncoding><!--访问路径编解码字符集--> </configuration>
Post请求
与Get请求处理方法一样
Post请求中出现中文乱码:
通过配置过滤器来进行处理中文乱码:也就是初始化 Web 服务器时配置乱码处理的过滤器,并设置编码格式为 utf-8
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}; } }
CharacterEncodingFilter是在spring-web包中,所以用之前需要导入对应的jar包
五种类型请求参数传递
前面我们已经能够使用GET或POST来发送请求和数据,所携带的数据都是比较简单的数据,接下来在这个基础上,我们来研究一些比较复杂的参数传递,常见的参数种类有:
-
普通参数
-
POJO类型参数
-
嵌套POJO类型参数
-
数组类型参数
-
集合类型参数
普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数。
如果存在参数名不一样的情况下,则需要使用 @RequestParam 注解来解决参数问题
@RequestMapping("/commonParamDifferentName") @ResponseBody public String commonParamDifferentName(@RequestPaam("name") String userName,int age) { System.out.println("普通参数传递 userName ==> "+userName); System.out.println("普通参数传递 age ==> "+age); return"{'module':'common param different name'}"; }
POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数
后台接收参数:
//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递 @RequestMapping("/pojoParam") @ResponseBody public String pojoParam(User user){ System.out.println("pojo参数传递 user ==> "+user); return"{'module':'pojo param'}"; }
-
POJO参数接收,前端GET和POST发送请求数据的方式不变。
-
请求参数key的名称要和POJO中属性的名称一致,否则无法封装。
嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数
在 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...略 } }
后台接收参数:
发送的格式应该为: address.city address.provience
请求参数key的名称要和POJO中属性的名称一致,否则无法封装
//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递 @RequestMapping("/pojoParam") @ResponseBody public String pojoParam(User user){ System.out.println("pojo参数传递 user ==> "+user); return "{'module':'pojo param'}"; }
数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数
后台接收参数:
@RequestMapping("/arrayParam") @ResponseBody public String arrayParam(String[]likes){ System.out.println("数组参数传递 likes ==> "+Arrays.toString(likes)); return"{'module':'array param'}"; }
这种情况下会有错误的出现,SpringMVC 会将 List 当作一个POJO 的对象来处理,所以会报错,因此我们可以使用 @RequestParam注解
@RequestMapping("/listParam") @ResponseBody public String listParam(@RequestParam List<String> likes){ System.out.println("集合参数传递 likes ==> "+likes); return"{'module':'list param'}"; }
-
集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系
-
对于简单数据类型使用数组会比集合更简单些
名称 | @RequestParam |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法请求访问路径 |
相关参数 | value(默认),请求访问路径 |
JSON数据传输
对于JSON数据类型,我们常见的有三种:
-
json普通数组(["value1","value2","value3",...])
-
json对象({key1:value1,key2:value2,...})
-
json对象数组([{key1:value1,...},{key2:value2,...}])
JSON普通数组:
在SpringMVC的配置类中开启SpringMVC的注解支持,这里面就包含了将JSON转换成对象的功能
@Configuration @ComponentScan("com.itheima.controller") //开启json数据类型自动转换 @EnableWebMvc public class SpringMvcConfig { }
//使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据 @RequestMapping("/pojoParamForJson") @ResponseBody public String pojoParamForJson(@RequestBody User user){ System.out.println("pojo(json)参数传递 user ==> "+user); return "{'module':'pojo for json param'}"; }
SpringMVC接收JSON数据的实现步骤为:
(1)导入jackson包
(2)使用PostMan发送JSON数据
(3)开启SpringMVC注解驱动,在配置类上添加@EnableWebMvc注解
(4)Controller方法的参数前添加@RequestBody注解
名称 | @EnableWebMvc |
---|---|
类型 | 配置类注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 开启SpringMVC多项辅助功能 |
@RequestBody
名称 | @ResponseBody |
---|---|
类型 | 类注解或方法注解 |
位置 | SpringMVC控制器类或方法定义上方 |
作用 | 设置当前控制器方法响应内容为当前返回值,无需解析 |
@RequestBody与@RequestParam区别
区别
@RequestParam用于接收url地址传参,表单传参【application/x-www-formurlencoded】
@RequestBody用于接收json数据【application/json】
应用
后期开发中,发送json格式数据为主,@RequestBody应用较广如果发送非json格式数据,选用@RequestParam接收请求参数
日期类型参数传递
SpringMVC默认支持的字符串转日期的格式为yyyy/MM/dd,如果想传入别的值则可以使用 @DateTimeFormat 注解
@RequestMapping("/dataParam") @ResponseBody public String dataParam(Date date, @DateTimeFormat(pattern = "yyyy-MM-dd") Date date1){ System.out.println("参数传递 date ==> "+date); System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1); return"{'module':'data param'}"; }
@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:指定日期时间格式字符串 |
内部实现原理
讲解内部原理之前,我们需要先思考个问题:
前端传递字符串,后端使用日期Date接收
前端传递JSON数据,后端使用对象接收
前端传递字符串,后端使用Integer接收
后台需要的数据类型有很多中
在数据的传递过程中存在很多类型的转换
问:谁来做这个类型转换?
答:SpringMVC
问:SpringMVC是如何实现类型转换的?
答:SpringMVC中提供了很多类型转换接口和实现类
-
Converter接口,Converter所属的包为org.springframework.core.convert.converter,
请求参数年龄数据(String→Integer)
日期格式转换(String → Date)
-
HttpMessageConverter接口,该接口是实现对象与JSON之间的转换工作
注意:SpringMVC的配置类把@EnableWebMvc当做标配配置上去,不要省略
响应
SpringMVC接收到请求和数据后,进行一些了的处理,当然这个处理可以是转发给Service,Service层再调用Dao层完成的,不管怎样,处理完以后,都需要将结果告知给用户
对于响应,主要就包含两部分内容:
-
响应页面
-
响应数据
-
文本数据
-
json数据
-
因为异步调用是目前常用的主流方式,所以我们需要更关注的就是如何返回JSON数据
响应页面:
@Controller public class UserController { @RequestMapping("/toJumpPage") //注意 //1.此处不能添加@ResponseBody,如果加了该注入,会直接将page.jsp当字符串返回前端 //2.方法需要返回 String public String toJumpPage(){ System.out.println("跳转页面"); return "page.jsp"; } }
返回文本数据:
@Controller public class UserController { @RequestMapping("/toText") //注意此处该注解就不能省略,如果省略了,会把response text当前页面名称去查找,如果没有 回报404错误 @ResponseBody public String toText(){ System.out.println("返回纯文本数据"); return "response text"; } }
响应JSON数据:
响应Pojo对象:
@Controller public class UserController { @RequestMapping("/toJsonPOJO") @ResponseBody public User toJsonPOJO() { System.out.println("返回json对象数据"); User user = new User(); user.setName("itcast"); user.setAge(15); return user; } }
返回值为实体类对象,设置返回值为实体类类型,即可实现返回对应对象的json数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
响应POJO集合对象:
@Controller public class UserController { @RequestMapping("/toJsonList") @ResponseBody public List<User> toJsonList() { System.out.println("返回json集合数据"); User user1 = new User(); user1.setName("传智播客"); user1.setAge(15); User user2 = new User(); user2.setName("黑马程序员"); user2.setAge(12); List<User> userList = new ArrayList<User>(); userList.add(user1); userList.add(user2); return userList; } }
@ResponseBody
名称 | @ResponseBody |
---|---|
类型 | 方法\类注解 |
位置 | SpringMVC控制器方法定义上方和控制类上 |
作用 | 设置当前控制器返回值作为响应体,写在类上,该类的所有方法都有该注解功能 |
说明:
-
该注解可以写在类上或者方法上
-
写在类上就是该类下的所有方法都有@ReponseBody功能
-
当方法上有@ReponseBody注解后
-
方法的返回值为字符串,会将其作为文本内容直接响应给前端
-
方法的返回值为对象,会将对象转换成JSON响应给前端
-
此处又使用到了类型转换,内部还是通过Converter接口的实现类完成的,所以Converter除了前面所说的功能外,它还可以实现:
-
对象转Json数据(POJO -> json)
-
集合转Json数据(Collection -> json)
Rest风格
对于Rest风格,我们需要学习的内容包括:
REST简介
REST入门案例
REST快速开发
案例:基于RESTful页面数据交互
Rest简介
-
REST(Representational State Transfer),表现形式状态转换,它是一种软件架构风格当我们想表示一个网络资源的时候,可以使用两种方式:
-
传统风格资源描述形式
-
http://localhost/user/getById?id=1 查询id为1的用户信息
-
-
REST风格描述形式
-
传统方式一般是一个请求url对应一种操作,这样做不仅麻烦,也不安全,因为会程序的人读取了你的请求url地址,就大概知道该url实现的是一个什么样的操作,查看REST风格的描述,你会发现请求地址变的简单了,并且光看请求URL并不是很能猜出来该URL的具体功能
所以REST的优点有:
-
隐藏资源的访问行为,无法通过地址得知对资源是何种操作
-
书写简化
一个相同的url地址即可以是新增也可以是修改或者查询,那么到底我们该如何区分该请求到底是什么操作?
-
按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
-
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,按照不同的请求方式代表不同的操作类型
-
发送GET请求是用来做查询
-
发送POST请求是用来做新增
-
发送PUT请求是用来做修改
-
发送DELETE请求是用来做删除
但是注意:
-
上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范
-
REST提供了对应的架构方式,按照这种架构设计项目可以降低开发的复杂性,提高系统的可伸缩性
-
REST中规定GET/POST/PUT/DELETE针对的是查询/新增/修改/删除,但是我们如果非要用GET请求做删除,这点在程序上运行是可以实现的
-
但是如果绝大多数人都遵循这种风格,你写的代码让别人读起来就有点莫名其妙了。
-
-
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts......
清楚了什么是REST风格后,我们后期会经常提到一个概念叫RESTful,那什么又是RESTful呢?
-
根据REST风格对资源进行访问称为RESTful。
后期我们在进行开发的过程中,大多是都是遵从REST风格来访问我们的后台服务,所以可以说咱们以后都是基于RESTful来进行开发的。
Restful开发
需求:将之前的增删改查替换成RESTful的开发方式。
1.之前不同的请求有不同的路径,现在要将其修改为统一的请求路径
修改前: 新增: /save ,修改: /update,删除 /delete...
修改后: 增删改查: /users
2.根据GET查询、POST新增、PUT修改、DELETE删除对方法的请求方式进行限定
3.发送请求的过程中如何设置请求参数?
新增:
@Controller public class UserController { //设置当前请求方法为POST,表示REST风格中的添加操作 @RequestMapping(value = "/users", method = RequestMethod.POST) @ResponseBody public String save() { System.out.println("user save..."); return "{'module':'user save'}"; } }
-
将请求路径更改为/users
-
访问该方法使用 POST: http://localhost/users
-
-
使用method属性限定该方法的访问方式为POST
-
如果发送的不是POST请求,比如发送GET请求,则会报错
-
删除:
@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'}"; } }
-
将请求路径更改为/users
-
访问该方法使用 DELETE: http://localhost/users
-
访问成功,但是删除方法没有携带所要删除数据的id,所以针对RESTful的开发,如何携带数据参数?
前端发送请求的时候使用: http://localhost/users/1 ,路径中的1就是我们想要传递的参数
后端获取参数,需要做如下修改:
-
修改@RequestMapping的value属性,将其中修改为/users/{id},目的是和路径匹配
-
在方法的形参前添加@PathVariable注解
@Controller public class UserController { //设置当前请求方法为DELETE,表示REST风格中的删除操作 @RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE) @ResponseBody public String delete(@PathVariable("id") Integer id) { System.out.println("user delete..." + id); return "{'module':'user delete'}"; } } }
当形参与 value 中参数一样时,可以忽略 PathVariable 中的属性
前端发送请求的时候使用: http://localhost/users/1/tom ,路径中的1和tom就是我们想要传递的两个参数
@Controller public class UserController { //设置当前请求方法为DELETE,表示REST风格中的删除操作 @RequestMapping(value = "/users/{id}/{name}", method = RequestMethod.DELETE) @ResponseBody public String delete(@PathVariable Integer id, @PathVariable String name) { System.out.println("user delete..." + id + "," + name); 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'}"; } }
-
将请求路径更改为/users
-
访问该方法使用 PUT: http://localhost/users
-
-
访问并携带参数: 使用 json 的形式传入参数
根据ID查询:
@Controller public class UserController { //设置当前请求方法为GET,表示REST风格中的查询操作 @RequestMapping(value = "/users/{id}", method = RequestMethod.GET) @ResponseBody public String getById(@PathVariable Integer id) { System.out.println("user getById..." + id); return "{'module':'user getById'}"; } } }
将请求路径更改为/users
-
访问该方法使用 GET: http://localhost/users/666
查询所有:
@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'}"; } } }
将请求路径更改为/users
-
访问该方法使用 GET: http://localhost/users
小结
(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){
}
@PathVariable
名称 | @PathVariable |
---|---|
类型 | 形参注解 |
位置 | SpringMVC控制器方法形参定义前面 |
作用 | 绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应 |
关于接收参数,我们学过三个注解@RequestBody、@RequestParam、@PathVariable ,这三个注解之间的区别和应用分别是什么?
-
区别
-
@RequestParam用于接收url地址传参或表单传参
-
@RequestBody用于接收json数据
-
@PathVariable用于接收路径参数,使用{参数名称}描述路径参数
-
-
应用
-
后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
-
如果发送非json格式数据,选用@RequestParam接收请求参数
-
采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值
-
RESTful快速开发
做完了RESTful的开发,你会发现好麻烦,麻烦在哪?
问题1:每个方法的@RequestMapping注解中都定义了访问路径/books,重复性太高
将@RequestMapping提到类上面,用来定义所有方法共同的访问路径。
问题2:每个方法的@RequestMapping注解中都要使用method属性定义请求方式,重复性太高
使用@GetMapping @PostMapping @PutMapping @DeleteMapping代替
问题3:每个方法响应json都需要加上@ResponseBody注解,重复性太高
1.将ResponseBody提到类上面,让所有的方法都有@ResponseBody的功能 2.使用@RestController注解替换@Controller与@ResponseBody注解,简化书写
所以我们可以这样:
@RestController//@Controller + ReponseBody @RequestMapping("/books") public class BookController { //@RequestMapping(method = RequestMethod.POST) @PostMapping public String save(@RequestBody Book book) { System.out.println("book save..." + book); return "{'module':'book save'}"; } //@RequestMapping(value = "/{id}", method = RequestMethod.DELETE) @DeleteMapping("/{id}") public String delete(@PathVariable Integer id) { System.out.println("book delete..." + id); return "{'module':'book delete'}"; } //@RequestMapping(method = RequestMethod.PUT) @PutMapping public String update(@RequestBody Book book) { System.out.println("book update..." + book); return "{'module':'book update'}"; } //@RequestMapping(value = "/{id}",method = RequestMethod.GET) @GetMapping("/{id}") public String getById(@PathVariable Integer id) { System.out.println("book getById..." + id); return "{'module':'book getById'}"; } //@RequestMapping(method = RequestMethod.GET) @GetMapping public String getAll() { System.out.println("book getAll..."); return "{'module':'book getAll'}"; } }
@RestController
名称 | @RestController |
---|---|
类型 | 类注解 |
位置 | 基于SpringMVC的RESTful开发控制器类定义上方 |
作用 | 设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能 |
@GetMapping @PostMapping @PutMapping @DeleteMapping
名称 | @GetMapping @PostMapping @PutMapping @DeleteMapping |
---|---|
类型 | 方法注解 |
位置 | 基于SpringMVC的RESTful开发控制器方法定义上方 |
作用 | 设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应GET请求 |
相关属性 | value(默认):请求访问路径 |
SpringMVC拦截静态资源
SpringMVC之所以拦截静态资源是因为配置ServletContainerInitConfig的时候设置拦截路径为 “ / ” 所以会拦截所有的路径,解决方案为:
-
SpringMVC需要将静态资源进行放行。
@Configuration public class SpringMvcSupport extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/pages/**").addResourceLocations("/pages/"); registry.addResourceHandler("/js/**").addResourceLocations("/js/"); registry.addResourceHandler("/css/**").addResourceLocations("/css/"); registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/"); } }
-
该配置类是在config目录下,SpringMVC扫描的是controller包,所以该配置类还未生效,要想生效需要将SpringMvcConfig配置类进行修改
@Configuration @ComponentScan({"com.itheima.controller", "com.itheima.config"}) @EnableWebMvc public class SpringMvcConfig { } //或者 @Configuration @ComponentScan("com.itheima") @EnableWebMvc public class SpringMvcConfig { }