SpringMVC 控制层框架-上
一、SpringMVC简介
1. 介绍
Spring Web MVC 是基于Servlet API构建的原始Web框架,从一开始就包含在Spring Framework 中。在控制层框架经历Srust、WebWork、Strust2等诸多产品的历代更迭之后,目前业界普遍选择了SpringMVC 作为Java EE项目表述层开发的首选方案。之所以能做到这一点,是因为SpringMVC 具备以下优势:
- Spring 家族原生产品,与IOC容器等基础设施无缝对接
- 表述层各细分领域需要解决的问题全方位覆盖,提供全面解决方案
- 代码清新简洁,大幅度提升开发效率
- 内部组件化程度高,可插拔式组件即插即用,想要什么功能配置相应组件即可
- 性能卓著,尤其适合现代大型、超大型互联网项目要求
2. 主要作用
SSM框架构建起单体项目的技术栈需求,其中的SpringMVC负责表述层(控制层)实例简化
SpringMVC的作用主要覆盖的是表述层,例如:请求映射、数据输入、视图界面、请求分发、表单、回显、会话控制、过滤拦截、异步交互、文件上传、文件下载、数据校验、类型转换等等
最终总结:
- 简化前端参数接收(形参列表)
- 简化后端数据响应(返回值)
3. 核心组件和调用流程理解
SpringMVC 与许多其他Web框架一样,是围绕前端控制器模拟设计的,其中中央Servlet DispatcherServlet 做整体请求处理调度,除了DispatcherServlet SpringMVC其他特殊组件协作完成请求处理和响应呈现。
SpringMVC涉及组件理解:
- DispatcherServlet:SpringMVC提供,我们需要使用web.xml 配置时其生效,它是整个流程处理的核心,所有请求都经过它的处理和分发!(CEO)
- HandlerMapping:SpringMVC提供,我们需要进行 IoC配置使其加入 IoC容器方可生效,它内部缓存 handler(Controller方法)和 handler访问路径数据,被DispathcherServlet调用,用于查找路径对应的 handler。(秘书)
- HandlerAdapter:SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它可以处理请求参数和处理响应数据,每次DispatcherServlet都是通过handlerAdapeter间接调用handler,他是handler和DisopatcherServlet之间的适配器。(经理)
- Handler:handler又称处理器,他是Controller类内部的方法简称,是由我们自己定义,用来接收参数,向后调用业务,最终返回响应结果。(打工人)
- ViewResolver:SpringMVC提供,我们需要进行 IoC 配置使其加入 IoC 容器方可生效,视图解析器主要作用简化模板视图页面查找的,但是需要注意,前后端分离项目,后端只返回JSON 数据,不返回页面,那就不需要视图解析器,所以,视图解析器相对其他的组件不是必须的。(财务)
二、SpringMVC接收数据
1. 访问路径设置
@RequestMapping 注解的作用就是将请求的 URL 地址和处理请求的方式(handler 方法)关联起来,建立映射关系。SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的方法来处理这个请求。
1.1 精准路径匹配
在 @RequestMapping 注解指定 URL 地址时,不使用任何通配符,按照请求地址进行精准匹配。
@Controller public class UserController { /** * 精准设置访问地址 /user/login */ @RequestMapping(value = {"/user/login"}) @ResponseBody public String login(){ System.out.println("UserController.login"); return "login success!!"; } /** * 精准设置访问地址 /user/register */ @RequestMapping(value = {"/user/register"}) @ResponseBody public String register(){ System.out.println("UserController.register"); return "register success!!"; } }
1.2 模糊路径匹配
在 @RequestMapping 注解指定 URL 地址时,通过使用通配符,匹配多个类似的地址。
@Controller public class ProductController { /** * 路径设置为 /product/* * /* 为单层任意字符串 /product/a /product/aaa 可以访问此handler * /product/a/a 不可以 * 路径设置为 /product/** * /** 为任意层任意字符串 /product/a /product/aaa 可以访问此handler * /product/a/a 也可以访问 */ @RequestMapping("/product/*") @ResponseBody public String show(){ System.out.println("ProductController.show"); return "product show!"; } } 单层匹配和多层匹配: /*:只能匹配URL地址中的一层,如果想准确匹配两层,那么就写“/*/*”以此类推。 /**:可以匹配URL地址中的多层。 其中所谓的一层或多层是指一个URL地址字符串被“/”划分出来的各个层次 这个知识点虽然对于@RequestMapping注解来说实用性不大,但是将来配置拦截器的时候也遵循这个规则。
1.3 类和方法级别区别
@RequestMapping 注解可以用于类级别和方法级别,它们之间的区别:
- 设置到类级别:@RequestMapping 注解可以设置在控制器类上,用于映射整个控制器的通用请求路径。这样,如果控制器中的多个方法都需要映射同一请求路径,就不需要在每个方法上添加映射路径。
- 设置到方法级别:@RequestMapping 注解也可以单独设置在控制器方法上,用于更细粒度地映射请求路径和处理方法。当多个方法处理同一个路径的不同操作时,可以使用方法级别的 @RequestMapping 注解进行更精细的映射。
//1.标记到handler方法 @RequestMapping("/user/login") @RequestMapping("/user/register") @RequestMapping("/user/logout") //2.优化标记类+handler方法 //类上 @RequestMapping("/user") //handler方法上 @RequestMapping("/login") @RequestMapping("/register") @RequestMapping("/logout")
1.4 附带请求方式限制
HTTP 协仪定义了八种请求方式,在SpringMVC 中封装了下面这个枚举类:
public enum RequestMethod { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE }
默认情况下:@RequestMapping("/logout") 任何请求方式都可以访问。
如果需要特定指定:
@Controller public class UserController { /** * 精准设置访问地址 /user/login * method = RequestMethod.POST 可以指定单个或者多个请求方式! * 注意:违背请求方式会出现405异常! */ @RequestMapping(value = {"/user/login"} , method = RequestMethod.POST) @ResponseBody public String login(){ System.out.println("UserController.login"); return "login success!!"; } /** * 精准设置访问地址 /user/register */ @RequestMapping(value = {"/user/register"},method = {RequestMethod.POST,RequestMethod.GET}) @ResponseBody public String register(){ System.out.println("UserController.register"); return "register success!!"; } }
注意:违背请求方式,会出现405异常。
1.5 进阶注解
还有 @RequestMapping 的 HTTP 方法特定快捷方式变体:
@GetMapping 、@PostMapping 、@PutMapping 、@DeleteMapping 、@PatchMapping
@RequestMapping(value="/login",method=RequestMethod.GET) || @GetMapping(value="/login")
注意:进阶注解只能添加到 handler 方法上,无法添加到类上。
1.6 常见配置问题
出现原因:多个handler 方法映射了同一个地址,导致 SpringMVC 在接收到这个地址的请求时该找哪个 handler 方法处理。
There is already 'demo03MappingMethodHandler' bean method com.atguigu.mvc.handler.Demo03MappingMethodHandler#empGet() mapped.
2. 接收参数(重点)
2.1 param 和 json参数比较
在HTTP 请求中,我们可以选择不同的参数类型,如 param类型和JSON 类型。两种参数的区别和对比:
- 参数编码:param 类型的参数会被编码为 ASCII 码。而JSON 类型的参数会被编码为 UTF-8。
- 参数顺序:param 类型的参数没有顺序限制。JSON类型的参数是有序的。JSON采用键值对的形式进行传递,其中键值对是有序排列的。
- 数据类型:param 类型的参数仅支持字符串类型、数值类型和布尔类型等简单数据类型。而JSON类型的参数则支持更复杂的数据类型,如数组、对象等。
- 嵌套性:param 类型的参数不支持嵌套。JSON类型的参数支持嵌套,可以传递更复杂的数据结构。
- 可读性:param 类型的参数格式比JSON类型的参数更加简单、易读。JSON格式在传递嵌套数据结构时更加清晰易懂。
总的来说,param 类型的参数适用于单一的数据传递,而JSON 类型的参数则更适用于更复杂的数据结构传递。根据具体的业务需求,需要选择合适的参数类型。在实际开发中,常见的做法是:在GET 请求中采用 param 类型的参数,而在 POST 请求中采用 JSON 类型的参数传递。
2.2 param参数接收
1)直接接值
客户端请求
handler接收参数只要形参名和类型与传递参数相同,即可自动接收。
@Controller @RequestMapping("param") public class ParamController { /** * 前端请求: http://localhost:8080/param/value?name=xx&age=18 * * 可以利用形参列表,直接接收前端传递的param参数! * 要求: 参数名 = 形参名 * 类型相同 * 出现乱码正常,json接收具体解决!! * @return 返回前端数据 */ @GetMapping(value="/value") @ResponseBody public String setupForm(String name,int age){ System.out.println("name = " + name + ", age = " + age); return name + age; } }
2)@RequestParam注解
可以使用 @RequestParam 注解将 Servlet 请求参数(即查询参数或表单数据)绑定到控制器中的方法参数。
@RequestParam使用场景:
- 指定绑定的请求参数名
- 要求请求参数必须传递
- 为请求参数提供默认值
/** * 前端请求: http://localhost:8080/param/data?name=xx&stuAge=18 * * 使用@RequestParam注解标记handler方法的形参 * 指定形参对应的请求参数@RequestParam(请求参数名称) */ @GetMapping(value="/data") @ResponseBody public Object paramForm(@RequestParam("name") String name, @RequestParam("stuAge") int age){ System.out.println("name = " + name + ", age = " + age); return name+age; }
默认情况下,使用此批注方法参数是必需的,但你可以通过将 @RequestParam 批注的 required 属性设置为 false。如果没有设置非必须,也没有传递参数会出现 400 状态码。
将参数设置非必须,并且设置默认值:
@GetMapping(value="/data") @ResponseBody public Object paramForm(@RequestParam("name") String name, @RequestParam(value = "stuAge",required = false,defaultValue = "18") int age){ System.out.println("name = " + name + ", age = " + age); return name+age; }
3)特殊场景接值
① 一名多值
多选框,提交的数据的时候一个key对应多个值,我们可以使用集合进行接收。
/** * 前端请求: http://localhost:8080/param/mul?hbs=吃&hbs=喝 * * 一名多值,可以使用集合接收即可!但是需要使用@RequestParam注解指定 */ @GetMapping(value="/mul") @ResponseBody public Object mulForm(@RequestParam List hbs){ System.out.println("hbs = " + hbs); return hbs; }
② 实体接收
Spring MVC 是Spring 框架提供的 Web 框架,它允许开发者使用实体对象来接收 HTTP 请求中的参数。通过这种方式,可以在方法内部直接使用对象的属性来访问请求参数,而不需要每个参数都写一遍。
public class User { private String name; private int age = 18; // getter 和 setter 略 } @Controller @RequestMapping("param") public class ParamController { @RequestMapping(value = "/user", method = RequestMethod.POST) @ResponseBody public String addUser(User user) { // 在这里可以使用 user 对象的属性来接收请求参数 System.out.println("user = " + user); return "success"; } }
上述代码中,将请求参数 name 和 age 映射到实体类属性上,要求属性名必须等于参数名,否则无法映射。
使用postman传递参数测试:
2.3 路径参数接收
路径传递参数是一种在 URL 路径中传递参数的方式,在RESTFUL的 Web 应用程序中,经常使用路径传递参数来表资源的唯一标识符或更复杂的标识方式。而Spring MVC 框架提供了 @PathVariable 注解来处理路径传递参数。
@PathVariable 注解允许将 URL 中的占位符映射到控制器方法中的参数。
/** * 动态路径设计: /user/{动态部分}/{动态部分} 动态部分使用{}包含即可! {}内部动态标识! * 形参列表取值: @PathVariable Long id 如果形参名 = {动态标识} 自动赋值! * @PathVariable("动态标识") Long id 如果形参名 != {动态标识} 可以通过指定动态标识赋值! * * 访问测试: /param/user/1/root -> id = 1 uname = root */ @GetMapping("/user/{id}/{name}") @ResponseBody public String getUser(@PathVariable Long id, @PathVariable("name") String uname) { System.out.println("id = " + id + ", uname = " + uname); return "user_detail"; }
2.4 json参数
前端传递 JSON 数据时,Spring MVC 框架可以使用 @RequestBody 注解来将 JSON 数据转换为 java 对象。@RequestBody 注解表示当前方法参数的值应该从请求体中获取,并且需要指定 value 属性来指示请求体应该映射到哪个参数上。
1)前端发送 JSON 数据的示例:(使用postman 测试)
{ "name": "张三", "age": 18, "gender": "男" }
2)定义一个用于接收 JSON 数据的 java类。
public class Person { private String name; private int age; private String gender; // getter 和 setter 略 }
3)在控制器中,使用 @RequestBody 注解来接收 JSON 数据,并将其转换为 Java对象。
@PostMapping("/person") @ResponseBody public String addPerson(@RequestBody Person person) { // 在这里可以使用 person 对象来操作 JSON 数据中包含的属性 return "success"; }
在上述代码中,@RequestBody 注解将请求体中的 JSON 数据映射到 Person 类型的 person参数并将其作为一个对象来传递给 addPerson() 方法处理。
注意: ${msg}