34、请求处理-【源码分析】-Model、Map原理
34、请求处理-【源码分析】-Model、Map原理
在 Spring Boot 中,处理请求时,控制器方法可以接收 `Model` 和 `Map` 类型的参数,用于向视图传递数据。以下是 `Model` 和 `Map` 参数处理的原理分析:
### 1. 参数解析过程
#### **1.1 确定参数解析器**
当 Spring MVC 处理请求时,会遍历所有注册的 `HandlerMethodArgumentResolver` 实例,调用其 `supportsParameter` 方法,判断是否支持当前参数类型。
对于 `Model` 和 `Map` 类型的参数:
- `ModelMethodProcessor` 和 `MapMethodProcessor` 这两个解析器会返回 `true`,因为它们支持处理这两种类型的参数。
#### **1.2 解析参数**
一旦确定了合适的参数解析器,就会调用其 `resolveArgument` 方法来解析参数:
- **`ModelMethodProcessor`**
```java
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
return mavContainer.getModel();
}
```
- **`MapMethodProcessor`**
```java
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
return mavContainer.getModel();
}
```
这两个解析器都返回了 `mavContainer.getModel()`,这意味着它们实际上使用的是同一个对象。
### 2. `ModelAndViewContainer` 的作用
`ModelAndViewContainer` 是一个重要的容器,用于存储模型数据和视图信息。在请求处理过程中:
- **获取 `Model` 对象**
`mavContainer.getModel()` 方法会返回一个 `Model` 实例,通常是一个 `BindingAwareModelMap` 对象,它既是 `Model` 也是 `Map`。
- **数据共享**
由于 `Model` 和 `Map` 参数都指向同一个 `BindingAwareModelMap` 对象,因此对它们的操作会互相影响。
### 3. 数据存储与传递
#### **3.1 添加数据**
在控制器方法中,通过以下方式添加数据:
- **使用 `Model`**
```java
model.addAttribute("name", "张三");
```
- **使用 `Map`**
```java
map.put("age", 18);
```
这些数据都会被添加到 `BindingAwareModelMap` 中。
#### **3.2 数据传递到视图**
请求处理完成后,Spring MVC 会将 `ModelAndViewContainer` 中的数据封装到 `ModelAndView` 对象中,然后调用视图渲染器进行渲染。
在渲染过程中,会调用 `exposeModelAsRequestAttributes` 方法:
```java
protected void exposeModelAsRequestAttributes(Map<String, Object> model,
HttpServletRequest request) throws Exception {
for (String name : model.keySet()) {
Object value = model.get(name);
if (value != null) {
request.setAttribute(name, value);
} else {
request.removeAttribute(name);
}
}
}
```
该方法会将模型数据中的所有属性添加到 `HttpServletRequest` 的请求域中,以便在视图中通过 `${name}` 表达式访问。
### 4. 示例代码
```java
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(Model model, Map<String, Object> map) {
model.addAttribute("name", "张三");
map.put("age", 18);
// 此时 model 和 map 指向同一个对象
System.out.println(model == map); // 输出 true
return "helloView";
}
}
```
在 `helloView.jsp` 中:
```jsp
<p>姓名:${name}</p>
<p>年龄:${age}</p>
```
### 5. 总结
- **`Model` 和 `Map` 参数在 Spring Boot 中本质上是同一个对象,都是 `BindingAwareModelMap` 的实例。**
- **通过 `Model` 或 `Map` 添加的数据最终都会被放入 `HttpServletRequest` 的请求域中,供视图访问。**
- **这种机制简化了数据传递的过程,使得开发者可以方便地在控制器和视图之间共享数据。**
---
通过以上分析,我们深入理解了 Spring Boot 中 `Model` 和 `Map` 参数的处理原理,为开发 Web 应用提供了更清晰的思路。