SpringMVC简介
SpringMvc概述
1. SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
1). 底层基于Spring
2). 封装了web三大组件(Servlet,Filter,Listener)
2. 优点
1). 使用简单,开发便捷(相比于Servlet)
2). 灵活性强
SpringMVC相关依赖
<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>
初始化SpringMVC环境(同Spring)
//springmvc配置类,本质上还是一个spring配置类
@Configuration
@ComponentScan("com.itheima.controller")
public class SpringMvcConfig {
}
创建SpringMVC控制器类(等同于Servlet功能)
//定义表现层控制器bean
@Controller
public class UserController {
//设置映射路径为/save,即外部访问路径
@RequestMapping("/save")
//设置当前操作返回结果为字符串
@ResponseBody
public String save(){
System.out.println("user save ...");
return "hello";
}
}
初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC请求拦截的路径
//AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类
//web容器配置类
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
//加载springmvc配置类,产生springmvc容器(本质还是spring容器)
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;
}
}
核心API
@Controller
1. 类型
:
类注解
2. 位置
:SpringMVC控制器类定义上方
3. 作用
:设定SpringMVC的核心控制器bean
4. 范例
:
@Controller
public class UserController {
}
@RequestMapping
1. 类型
:方法注解
2. 位置
:SpringMVC控制器方法定义上方
3. 作用
:设置当前控制器方法请求访问路径
说白了就是建立请求路径与处理器方法之间的映射关系
4. 范例:
@RequestMapping("/save")
public void save(){
System.out.println("user save ...");
}
@ResponseBody
1. 类型
:方法注解
2. 位置
:SpringMVC控制器方法定义上方
3. 作用
:设置当前控制器方法响应体内容为当前返回值,无需解析
说白了,就是将方法返回值序列化成字符串给前端返回;
4. 范例
:
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "hello";
}
基本案例流程分析
# 启动服务器初始化过程
0. tomcat -> ServletContainersInitConfig -> SpringMvcConfig -> UserController
1. tomcat的启动执时,根据SPI机制加载ServletContainersInitConfig类
1). spring-web.jar下有META-INF/services/javax.servlet.ServletContainerInitializer文件
2). 此文件中配置实现类 org.springframework.web.SpringServletContainerInitializer
3). 此类会被tomcat所加载(SPI机制),此类上的有个配置@HandlesTypes({WebApplicationInitializer.class})
4). 此接口WebApplicationInitializer的所有实现类都会被加载封装到set集合中,通过onStartup方法传入;
(看SpringServletContainerInitializer的onStartup)
5). 而我们入门案例中的ServletContainersInitConfig类就是WebApplicationInitializer实现类,所以也会被运行
2. ServletContainersInitConfig类的方法会被运行
1). createServletApplicationContext方法运行,加载springmvc配置类
2). getServletMappings方法运行, 给DispatcherServlet类设置访问路径为/ (表示拦截所有)
3. SpringMvcConfig类配置的注解生效
1). @ComponentScan("com.itheima.controller")
2). springmvc底层开始扫描 com.itheima.controller包
4. UserController类生效
1). @Controller
表示此bean会添加到springmvc的ioc容器
2). @RequestMapping("/save")
设置save方法的访问路径为 /save
3). @ResponseBody
方法的的返回值String将会通过响应体返回给前端
总之,springmvc通过spi机制调用 SpringServletContainerInitializer中的onstart方法来完成IOC容器以及核心调度器初始化的操作;
# 问题:
1. 入门案例是一个web项目,为何不写web.xml文件?
SPI机制: ServletContainerInitializer
2. 入门案例为何不写Servlet?
springMVC底层封装了DispatcherServlet,它拦截所有请求,然后分发给Controller.
单次请求过程
0. 浏览器(前端) -> (后端)tomcat -> DispatcherServlet -> UserController.save
1. 前端发送请求 http://localhost:8080/save
2. http://localhost:8080会找到tomcat(web容器-》servlet容器)
3. tomcat接收到请求,发现spingmvc中有个DispatcherServlet的拦截路径为/,所以就将请求交给DispatcherServlet,service方法会运行
4. DispatcherServlet会找到/save对应的控制器方法UserController.save方法,然后调用此方法
5. UserController.save方法运行之后,有String类型的返回值hello,通过响应体返回给前端
ServletContainersInitConfig的常用写法
public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
//加载springmvc配置的
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
//设置由springmvc控制器处理的请求映射路径
protected String[] getServletMappings() {
return new String[]{"/"};
}
//加载spring配置类
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
}
RequestMapping注解
作用:
RequestMapping 注解,用于建立请求路径与方法的对应关系。
2.1 编写位置
# 编写位置:
1. 类上:
窄化路径,访问此类中的方法时,必须加上类上的路径
2. 方法:
建立路径与方法的对应关系
@Controller
//类上方配置的请求映射与方法上面配置的请求映射连接在一起,形成完整的请求映射路径
@RequestMapping("/user")
public class UserController {
//请求路径映射
@RequestMapping("/save")
@ResponseBody
public String save(){
System.out.println("user save ...");
return "hello";
}
//请求路径映射
@RequestMapping("/delete")
@ResponseBody
public String delete(){
System.out.println("user delete ...");
return "hello";
}
}
2.2 常用属性
# @RequestMapping注解常用属性
1. value或者path: 用来指定虚拟路径,value=可以省略
2. method: 用来限定请求的方式 (restful风格)
1). 不写,默认什么请求方式都可以
2). 写了,指定了请求方式,如果不匹配就会响应405状态码(错误)
@Controller
@RequestMapping("/role")
public class RoleController {
@RequestMapping(value = "/save",
method = RequestMethod.GET)
@ResponseBody
public String save(){
System.out.println("RoleController save...");
return "hello";
}
@RequestMapping(value = "/delete",
method = RequestMethod.POST)
@ResponseBody
public String delete(){
System.out.println("RoleController delete...");
return "hello";
}
}
请求
# SpringMVC的请求处理可以分为两大类:
这里说的同步请求和异步请求指的是前端
1. 同步请求
0). 特点: 同步请求的响应内容会刷新整个网页
1). url格式的请求参数
2). 响应: 请求转发
2. 异步请求 (主流!!!)
0). 特点: 响应内容只会让网页局部刷新
ajax/json (封装库,框架 axios/vue...)
1). 请求参数
url格式 name=value&name=value...
json格式
2). 响应
字符串
json格式
# SpringMvc对请求和响应进行了封装
1. 我们的控制器方法可以接收请求参数
2. 如果请求携带以下类型的数据,SpringMVC会自动帮我们接收,并且解析之后传递给方法进行使用
3. 使用方式: 直接定义方法形参
4. 注意: 方法上的形参名称必须要和请求参数名称保持一致
1).基本数据类型和string
2).pojo类型
3).数组类型
4).集合类型
@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 "hello";
}
//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
System.out.println("pojo参数传递 user ==> "+user);
return "hello";
}
//嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递
@RequestMapping("/pojoContainPojoParam")
@ResponseBody
public String pojoContainPojoParam(User user){
System.out.println("pojo嵌套pojo参数传递 user ==> "+user);
return "hello";
}
//数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes){
System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));
return "hello";
}
//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
System.out.println("集合参数传递 likes ==> "+ likes);
return "hello";
}
}
获取json格式的请求参数
# json格式数据
1. 对象 { }
2. 数组 [ ]
# json格式请求参数常见类型
1. json数组
String [] / int[]
["a","b","c"]
2. json对象(POJO)
User
{"name":"zs","age" : 18}
3. json数组(POJO)
List<User>
[{"name":"zs","age" : 18},{"name":"ls","age":19}]
# java中的json转换工具
1. fastJson ( alibaba的 )
1). json格式字符串变成 pojo对象
2). pojo对象变成json格式字符串
2. JackSon (springMVC底层使用)
3. gson
# web阶段封装的一个工具类 BaseController
1. 接收json格式的请求参数 变成 pojo
2. 将pojo的响应数据 变成 json格式字符串
jsckson依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
开启自动抓换JSON的支持
@Configuration
@ComponentScan("com.itheima.controller")
//开启json数据类型自动转换
//@EnableWebMvc注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换
@EnableWebMvc
public class SpringMvcConfig {
}
设置接受JSON数据
@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'}";
}
核心代码
//集合参数:json格式
//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
//2.使用@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'}";
}
//POJO参数:json格式
//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
//2.使用@RequestBody注解将外部传递的json数据映射到形参的实体类对象中,要求属性名称一一对应
@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user){
System.out.println("pojo(json)参数传递 user ==> "+user);
return "{'module':'pojo for json param'}";
}
//集合参数:json格式
//1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
//2.使用@RequestBody注解将外部传递的json数组数据映射到形参的保存实体类对象的集合对象中,要求属性名称一一对应
@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'}";
}
核心API总结
@RequestBody与@RequestParam区别
1. 区别
@RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】
@RequestBody用于接收json数据【application/json】
2. 应用
后期开发中,发送json格式数据为主,@RequestBody应用较广
如果发送非json格式数据,选用@RequestParam接收请求参数
json格式请求参数
1. @RequestParam
接收集合类型的参数
1. 记得导入jackSon工具包(实现json和pojo之间的数据转换)
2. @EnableWebMvc
开启webmvc功能(功能之一: 自动实现json和pojo转换)
3. @RequestBody
在参数前面添加,用于接收json格式参数映射到pojo上
请求参数名称不一致
# @RequestParam
1. value : 指定前端的属性名映射到某个参数上
使用前提: 形参名跟前端name属性名不一致
2. required:用于指定此参数是否必传
true: (默认)表示必须要传,只要前端声明有name属性,不填属性值,也是有的
3. defaultValue:如果前端此参数值没有设置,这里参数会指定一个默认值。
4. //普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系
@RequestMapping("/commonParamDifferentName")
@ResponseBody
public String commonParamDifferentName(@RequestParam("name") String userName , int age){
System.out.println("普通参数传递 userName ==> "+userName);
System.out.println("普通参数传递 age ==> "+age);
return "{'module':'common param different name'}";
}
编码过滤器
//post请求乱码处理
@Override
protected Filter[] getServletFilters() {
//spring封装的过滤器, 拦截所有的请求,如果是post请求,就将编码修改为指定编码
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
日期处理
//日期参数
//使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd
@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'}";
}
响应
SpringMVC对响应的封装可以分为以下两类
- 响应页面
用于同步请求,了解即可
2. 响应数据
1). 文本数据
2). json数据(常用)
核心API
@ResponseBody
- 类型:方法注解|方法返回参数之上
- 位置:SpringMVC控制器方法定义上方
- 作用:设置当前控制器返回值作为响应体
- 只要配置好对应的环境 (需要导入jackson, 必须@EnableWebMvc)
那么springmvc会自动将javabean返回值转成json格式字符串
示例代码
@RequestMapping("/resp")
@ResponseBody
public User responsebody(@RequestBody User user){
System.out.println(user);
user.setName("小李");
return user;
}
Springmvc对Restful风格的支持
Restfu
- Rest( Representational State Transfer) 直接翻译的意思是"表现层的状态转化"。
一种网络资源的访问风格,定义了网络资源的访问方式
- Restful是按照Rest风格访问网络资源
- 传统风格访问路径
http://localhost:80/user/get?id=1
http://localhost/user/delete?id=1
- Rest风格访问路径
http://localhost/user/1
- 强调的点有2点:
- 路径即资源(路径中只能使用名词,不能使用动词)
- 用请求方式表达对资源的操纵行为
- 比如:http://localhost/user/1 请求方式:GET 表示查询id=1的用户信息
# 含义可以多个: 既可以是查询,也可以是删除,也可以是修改,也可以是添加 别人根本就不知道开发者在干嘛
优点
隐藏资源的访问行为,通过地址无法得知做的是何种操作(安全)
书写简化
Rest行为约定方式
Restful 使用URL定位资源,用HTTP请求方式(GET,POST,PUT,DELETE)描述操作
GET(查询) http://localhost/user GET
POST(保存) http://localhost/user POST
PUT(更新) http://localhost/user PUT
DELETE(删除) http://localhost/user DELETE
注意
:上述行为是约定方式,约定不是硬性规范,可以打破,所以称Rest风格,而不是Rest规范
Restful开发入门
@Controller
public class RestController {
@RequestMapping(value = "/rest",method = RequestMethod.GET)
@ResponseBody
public String demo01(){
System.out.println("findAll");
return "findAll";
}
@RequestMapping(value = "/rest/{id}",method = RequestMethod.GET)
@ResponseBody
public String demo11(@PathVariable Integer id){
System.out.println("findById : " + id);
return "findById";
}
@RequestMapping(value = "/rest/{page}/{pageSize}",method = RequestMethod.GET)
@ResponseBody
public String demo12(@PathVariable Integer page,@PathVariable Integer pageSize){
System.out.println("findByPage : " + page + "," + pageSize);
return "findByPage";
}
@RequestMapping(value = "/rest",method = RequestMethod.DELETE)
@ResponseBody
public String demo02(){
System.out.println("deleteAll");
return "deleteAll";
}
@RequestMapping(value = "/rest/{id}",method = RequestMethod.DELETE)
@ResponseBody
public String demo21(@PathVariable Integer id){
System.out.println("deleteById : " + id);
return "deleteById";
}
@RequestMapping(value = "/rest",method = RequestMethod.POST)
@ResponseBody
public String demo03(@RequestBody User user){
System.out.println("add: " + user);
return "add";
}
@RequestMapping(value = "/rest",method = RequestMethod.PUT)
@ResponseBody
public String demo04(){
System.out.println("update");
return "update";
}
}
Restful快速开发
/*
复合注解 : A和B注解作为C的元注解, 那么C= A+B
@RestController = @Controller + @ResponseBody
*/
//@Controller
//@ResponseBody // 表示每个方法响应数据都直接走响应体
@RestController
@RequestMapping("/rest")
public class RestfulController {
// @RequestMapping(value = "/rest",method = RequestMethod.GET)
// @GetMapping(value = "/rest")
@GetMapping
public String demo01(){
System.out.println("findAll");
return "findAll";
}
// @RequestMapping(value = "/rest/{id}",method = RequestMethod.GET)
@GetMapping("/{id}")
public String demo11(@PathVariable Integer id){
System.out.println("findById : " + id);
return "findById";
}
// @RequestMapping(value = "/rest/{page}/{pageSize}",method = RequestMethod.GET)
@GetMapping("/{page}/{pageSize}")
public String demo12(@PathVariable Integer page,@PathVariable Integer pageSize){
System.out.println("findByPage : " + page + "," + pageSize);
return "findByPage";
}
// @RequestMapping(value = "/rest",method = RequestMethod.DELETE)
@DeleteMapping
public String demo02(){
System.out.println("deleteAll");
return "deleteAll";
}
// @RequestMapping(value = "/rest/{id}",method = RequestMethod.DELETE)
@DeleteMapping("/{id}")
public String demo21(@PathVariable Integer id){
System.out.println("deleteById : " + id);
return "deleteById";
}
// @RequestMapping(value = "/rest",method = RequestMethod.POST)
@PostMapping
public String demo03(@RequestBody User user){
System.out.println("add: " + user);
return "add";
}
// @RequestMapping(value = "/rest",method = RequestMethod.PUT)
@PutMapping
public String demo04(){
System.out.println("update");
return "update";
}
}
总结
1.三层架构与MVC架构的关系?
三层架构包含数据访问层、业务逻辑层、web层(servlet html model);
MVC架构是对三层架构中的web层的再细粒度划分:解耦 提高代码复用 关注点分离;
2.sringmvc启动流程?
1.tomcat启动时,底层根据java提供的SPI机制,会自动加载spring-web-5.2.10.RELEASE.jar!\META-INF\services\javax.servlet.ServletContainerInitializer文件中的类:SpringServletContainerInitializer
并调用onStartup方法,该方法会收集当前项目中所有实现WebApplicationInitializer接口的类,并封装到set集合中农,然后注入onStartup方法中(SPI)
2.SpringServletContainerInitializer下的onStartup方法执行时,会循环调用一切实现WebApplicationInitializer接口的对象中的onStartup方法
3.我们工程中ServletContainersInitConfig最后也实现了WebApplicationInitializer,所以它的onStartup也会被调用,该方法会间接调用初始化IOC容器的方法和核心调度器DispatchServlet,并设置拦截规则“/”;
3.springmvc访问流程?
1.前端(浏览器)发送请求到后台,被tomcat接收
2.tomcat调用DispatchServlet核心调度器处理当前的请求;
3.DispatchServlet会根据请求的url地址找具体的处理器方法,并调用
4.处理器方法执行完毕,返回的数据被DispatchServlet响应给前端
4.springmvc请求相关的注解有哪些?
@ReqeustMapping 建立请求地址与处理器方法之间的关系,作用在类和方法之上;
属性:
value或者path:指定请求路径
method:指定请求方法 get delete put post等
有4中衍生注解:
@GetMapping @DeleteMapping @PutMapping @PostMapping
@Controller
@RestController=@Controller + @Responbody
关于参数处理的注解:
@ReqeustParam:解决请求参数与方法入参不一致的情况
属性:
value:指定请求参数名称
required: 默认是true,定义是否必须传入;
defaultValue:设置默认值
@RequestBoy:作用是接收前端ajax发送的json格式数据,并调用jackson工具自动实现json转java对象的功能
注解作用在方法的入参之上;
@DateTimeFormat(pattern="日志表达式")
要想使用springmvc的这些扩展功能,前提是开启:@EnalbeWebMvc
如何接收resetfull路径中的参数?
使用@PathVariable("指定路径参数的名称")
响应数据:
@ResponseBody:
特点:作用方法之上或者方法的返回值之上
作用:如果方法的返回值是基本类型+string,则直接返回
如果方法的返回值是一些负载的pojo