1.SpringMVC概述
SpringMVC功能与优点
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
SpringMVC技术与Servlet技术功能一样(对Servlet进行了封装),都属于Web层开发技术
SpringMVC的主要的作用就是用来接收前端发过来的请求和数据然后经过处理并将处理的结果响应给前端:
controller如何接收请求和数据- 如何将请求和数据转发给业务层
- 如何将响应数据转换成
json发回到前端
SpringMVC的优点:
- 使用简单、开发便捷(相比于
Servlet):上图为使用Servlet开发,下图为使用SpringMVC开发
- 灵活性强
SpringMVC和Servlet的三层架构
- 同步调用阶段:
- 浏览器发送一个请求给后端服务器(使用
Servlet来接收请求和数据)- 将后端服务器
Servlet拆分成三层,分别是web、service和dao(所有的处理都交给Servlet来处理导致耦合度高,对后期的维护和扩展不利)
web层主要由servlet来处理,负责页面请求和数据的收集以及响应结果给前端service层主要负责业务逻辑的处理dao层主要负责数据的增删改查操作- 针对
web层进行了优化(servlet处理请求和数据时,一个servlet只能处理一个请求),采用了MVC设计模式,将其设计为controller、view和Model
controller负责请求和数据的接收,接收后将其转发给service进行业务处理service根据需要会调用dao对数据进行增删改查dao把数据处理完后将结果交给service,service再交给controllercontroller根据需求组装成Model和View,二者组合起来生成页面转发给前端浏览器(controller可以处理多个请求,并对请求进行分发,执行不同的业务操作)- 异步调用阶段:同步调用的性能跟不上需求
- 异步调用时后端不需要返回
view视图,将其去除- 前端如果通过异步调用的方式进行交互,后台就需要将返回的数据转换成
json格式进行返回
2.SpringMVC入门
主要步骤
- 导入
SpringMVC坐标与Servlet坐标(scope设置为provided,具体原因见代码中的注释)- 创建
SpringMVC控制器类(等同于Servlet功能,具体代码见UserController.java)- 初始化
SpringMVC环境,设定SpringMVC加载对应的bean(具体代码见SpringMvcConfig.java)
@ResponseBody:设置当前控制器方法响应内容(json格式)为当前返回值,无需解析成某个资源在项目中的路径@RequestMapping:设置当前控制器方法请求访问路径- 初始化
Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求(具体代码见ServletContainersInitConfig.java,简化版本可见Springmvc_02_bean_load中的)
createServletApplicationContext:创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围(即整个web容器范围)中getServletMappings方法:设定SpringMVC对应的请求映射路径(即SpringMVC拦截哪些请求)createRootApplicationContext方法:如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行- 通过插件启动
tomcat(先按下图进行配置)
代码参考Springmvc_01_quickstart
工作流程解析
- 启动服务器初始化过程:
- 服务器启动,执行
ServletContainersInitConfig类,初始化web容器(类似于以前的web.xml)- 执行
createServletApplicationContext方法,创建WebApplicationContext对象- 加载
SpringMvcConfig配置类- 执行
@ComponentScan加载对应的bean- 加载
UserController,每个@RequestMapping的名称对应一个具体的方法(所有映射会放置在一起管理)- 执行
getServletMappings方法,设定SpringMVC拦截请求的路径规则
- 单次请求过程:
- 发送请求http://localhost/save
web容器发现该请求满足SpringMVC拦截规则,将请求交给SpringMVC处理- 解析请求路径
/save,由/save匹配执行对应的save方法并执行- 检测到有
@ResponseBody直接将save方法的返回值作为响应体返回给请求方
bean加载控制
在
com.psj包下有controller、service和dao等包,在SpringMVC的配置类SpringMvcConfig中扫描范围为controller,而在Spring的配置类SpringConfig中扫描的范围(com.psj)中包含了controller,如何避免Spring错误加载SpringMVC的bean?
Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等@ComponentScan({"com.psj.service","com.psj.dao"})
Spring加载的bean设定扫描范围为com.psj,但排除掉controller包// excludeFilters属性:设置扫描加载bean时,排除的过滤规则 // type属性:设置排除规则,当前使用按照bean定义时的注解类型进行排除 // classes属性:设置排除的具体注解类,当前设置排除@Controller定义的bean @ComponentScan(value = "com.psj", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) )
- 不区分
Spring与SpringMVC的环境,加载到同一个环境中代码参考Springmvc_02_bean_load
3.请求与响应
请求映射路径
团队多人开发时,每人设置在设置路径时出现冲突该如何解决?比如下面这种情况,启动
tomcat时会报错// UserController中存在一个save方法,路径设置为/save public class UserController { @RequestMapping("/save") @ResponseBody public String save(){ .... } } // BookController中存在一个save方法,路径设置也为/save public class BookController { @RequestMapping("/save") @ResponseBody public String save(){ .... } }需要在类上加
@RequestMapping注解进行优化,优化后的代码如下:@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/save") @ResponseBody public String save(){ ... } } @Controller @RequestMapping("/book") public class UserController { @RequestMapping("/save") @ResponseBody public String save(){ ... } }代码参考Springmvc_03_request_mapping
请求方式和参数
请求方式有哪几种?主要是
GET和POST接收到请求后,如何接收页面传递的参数?开发中发送
JSON格式数据为主,@RequestBody应用较广。如果发送非JSON格式数据,选用@RequestParam接收请求参数代码参考Springmvc_04_request_param
-
请求方式:
GET发送不同种类的参数:- 如果参数中传输中文导致接收到的参数出现乱码,可以在
pom.xml中添加<uriEncoding>)
- 如果参数中传输中文导致接收到的参数出现乱码,可以在
POST发送不同种类的参数:和GET请求一致(只不过参数写在请求体中)- 使用
PostMan发送POST请求时,选择form-data和x-www-form-urlencoded的区别在于后者可以传输文件 - 如果控制台打印出现中文乱码问题,可以在
ServletContainersInitConfig中配置过滤器
- 使用
-
请求参数:具体参考
UserController.java-
普通参数:
- 参数名与形参变量名相同时,定义形参即可接收参数
// http://localhost/commonParam?name=psj&age=18 @RequestMapping("/commonParam") @ResponseBody public String commonParam(String name ,int age){ ... }- 参数名与形参变量名不相同时,在形参前使用
@RequestParam注解
// http://localhost/commonParamDifferentName?name=psj&age=18 @RequestMapping("/commonParamDifferentName") @ResponseBody public String commonParamDifferentName(@RequestParam("name") String userName, int age){ ... } -
POJO类型参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数(请求参数key的名称要和POJO中属性的名称一致,否则无法封装)
// http://localhost/pojoParam?name=psj&age=18(User对象中包含这两个属性) @RequestMapping("/pojoParam") @ResponseBody public String pojoParam(User user){ System.out.println("pojo参数传递 user ==> "+user); return "{'module':'pojo param'}"; }- 嵌套
POJO类型参数:与POJO类型参数一致,只不过在发生请求时有所区别
// http://localhost/pojoContainPojoParam?name=psj&age=18&address.city=yunnan(User对象中包含这两个属性以及Address对象) @RequestMapping("/pojoContainPojoParam") @ResponseBody public String pojoContainPojoParam(User user){ ... }- 数组类型参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数
// http://localhost/arrayParam?likes=game&likes=ball(传入两个属性名一致的参数) @RequestMapping("/arrayParam") @ResponseBody public String arrayParam(String[] likes){ ... }- 集合类型参数:请求参数名与形参集合对象名相同且请求参数为多个,使用
@RequestParam绑定参数关系
// http://localhost/listParam?likes=game&likes=ball(传入两个属性名一致的参数) @RequestMapping("/listParam") @ResponseBody // SpringMVC将List看做是一个POJO对象来处理(相当于把likes视为List中的属性),但是List是一个接口无法创建对象,所以报错,需要使用@RequestParam public String listParam(@RequestParam List<String> likes){ System.out.println("集合参数传递 likes ==> "+ likes); return "{'module':'list param'}"; }-
JSON类型参数(GET和POST皆可):SpringMVC默认使用的是jackson来处理JSON的转换,所以需要在pom.xml添加jackson依赖。并且在PostMan中需要设置为Body然后再写入JSON数据。最后要在SpringMvcConfig中开启SpringMVC的注解支持(包含了将JSON转换成对象的功能)JSON普通数组:如{"game, "ball"}
@RequestMapping("/listParamForJson") @ResponseBody // 使用@RequestBody注解将外部传递的JSON数组数据映射到形参的集合对象中作为数据 public String listParamForJson(@RequestBody List<String> likes){ ... }JSON对象数据:如{"name":"psj", "age":18}
@RequestMapping("/pojoParamForJson") @ResponseBody public String pojoParamForJson(@RequestBody User user){ ... }JSON对象数组:如[{"name":"psj", "age":18},{"name":"psw", "age":18}]
@RequestMapping("/listPojoParamForJson") @ResponseBody public String listPojoParamForJson(@RequestBody List<User> list){ ... } -
日期类型参数:如果传入的参数为日期,并且每个参数的日期格式不固定
@RequestMapping("/dataParam") @ResponseBody // 使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd public String dataParam(Date date, @DateTimeFormat(pattern="yyyy-MM-dd") Date date1, @DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){ ... } -
响应
主要就包含两部分内容:
- 响应页面:不能添加
@ResponseBody注解- 响应数据:
- 文本数据:需要依赖
@ResponseBody注解(方法的返回值为字符串,会将其作为文本内容直接响应给前端)JSON数据:需要依赖@ResponseBody注解和@EnableWebMvc注解,同时要在pom.xml中添加jackson依赖包用于转换为JSON(方法的返回值为对象,会将对象转换成JSON响应给前端)代码参考Springmvc_05_response
4.REST风格
REST:Representational State Transfer,它是一种软件架构风格
RESTful:即根据REST风格对资源进行访问
简介
要表示一个网络资源的时候,可以使用两种方式:
- 传统风格资源描述形式:不仅麻烦,也不安全(通过
URL就可知道是什么操作)
- http://localhost/user/getById?id=1
- http://localhost/user/saveUser
- REST风格描述形式:简化请求地址,并且很难猜出该
URL的具体功能
- http://localhost/user/1
- http://localhost/user
一个相同的
URL可是新增也可是修改或者查询,如何区分该请求是什么操作?按照不同的请求方式代表不同的操作类型(这只是风格,并不是规范),比如发送http://localhost/users 请求时,根据发送时的请求方式即可区分
- 发送
GET请求是用来做查询- 发送
POST请求是用来做新增- 发送
PUT请求是用来做修改- 发送
DELETE请求是用来做删除
入门案例
代码参考Springmvc_06_rest中的
UserController.java注意事项:
- 如果有多个参数需要传递,比如前端发送请求http://localhost/users/1/psj
@Controller public class UserController { @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'}"; } }
形参中的注解
@RequestBody、@RequestParam、@PathVariable的区别和应用:
区别:
@RequestParam用于接收URL传参或表单传参@RequestBody用于接收JSON数据@PathVariable用于接收路径参数(即使用{参数名称}描述的路径参数)应用:
- 发送请求参数超过1个时,以
JSON格式为主,@RequestBody应用较广- 发送非
JSON格式数据,选用@RequestParam接收请求参数- 采用
RESTful进行开发,且参数数量只有1、2个时,可用@PathVariable接收请求路径变量
RESTful快速开发
在代码中有许多重复的地方,以下图为例:
- 每个方法的
@RequestMapping注解中都定义了访问路径/books- 每个方法的
@RequestMapping注解中都要使用method属性定义请求方式- 每个方法响应
JSON都需要加上@ResponseBody注解解决方式如下:
- 将
@RequestMapping提到类上面,用来定义所有方法共同的访问路径- 使用
@GetMapping、@PostMapping、@PutMapping和@DeleteMapping代替@RequestMapping注解及其method属性- 将
@ResponseBody提到类上面,此时类上有@Controller和@ResponseBody,可以使用@RestController替换这两个注解
代码参考Springmvc_06_rest中的BookController.java
5.案例
代码参考Springmvc_07_rest_case
注意事项:
- 页面访问处理:
- 当页面发送请求http://localhost/pages/book.html,正常来说应该显示
html页面,但是会显示404,这是因为SpringMVC拦截了静态资源(ServletContainersInitConfig中的getServletMappings方法),根据/pages/books.html去controller找对应的方法SpringMVC需要将静态资源进行放行(具体代码在SpringMvcSupport中),并且对SpringMvcConfig进行修改让其能扫描到该配置类
参考
https://www.bilibili.com/video/BV1Fi4y1S7ix?p=43-58























