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 再交给controller controller 根据需求组装成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 包
@ComponentScan(value = "com.psj",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
- 不区分
Spring 与SpringMVC 的环境,加载到同一个环境中
代码参考Springmvc_02_bean_load
3.请求与响应
请求映射路径
团队多人开发时,每人设置在设置路径时出现冲突该如何解决?比如下面这种情况,启动tomcat 时会报错
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String 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
-
普通参数:
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name ,int age){
...
}
- 参数名与形参变量名不相同时,在形参前使用
@RequestParam 注解
@RequestMapping("/commonParamDifferentName")
@ResponseBody
public String commonParamDifferentName(@RequestParam("name") String userName, int age){
...
}
-
POJO 类型参数:请求参数名与形参对象属性名相同,定义POJO 类型形参即可接收参数(请求参数key 的名称要和POJO 中属性的名称一致,否则无法封装)
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
System.out.println("pojo参数传递 user ==> "+user);
return "{'module':'pojo param'}";
}
- 嵌套
POJO 类型参数:与POJO 类型参数一致,只不过在发生请求时有所区别
@RequestMapping("/pojoContainPojoParam")
@ResponseBody
public String pojoContainPojoParam(User user){
...
}
- 数组类型参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes){
...
}
- 集合类型参数:请求参数名与形参集合对象名相同且请求参数为多个,使用
@RequestParam 绑定参数关系
@RequestMapping("/listParam")
@ResponseBody
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
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
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'}";
}
}
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
|