Spring MVC 中“拦截器”处理模型数据 (二) @ModelAttribute

简介: 在这里强烈建议看看我之前写的几篇关于SpringMVC的博客,都是串通的。@ModelAttribute这个是SpringMVC中处理模型数据的最难也是最重要的点。相当于以前Struct的拦截器。用途:比如我们要修改一个对象的部分数据,按照以前的思维,new一个对象保存数据,然后赋值,把不修改数据先拿出来保存起来。但是这个已经Out了, 在SpringMVC中,是拿到

在这里强烈建议看看我之前写的几篇关于SpringMVC的博客,都是串通的。

@ModelAttribute这个是SpringMVC中处理模型数据的最难也是最重要的点。相当于以前Struct的拦截器。

用途:比如我们要修改一个对象的部分数据,按照以前的思维,new一个对象保存数据,然后赋值,把不修改数据先拿出来保存起来。但是这个已经Out了, 在SpringMVC中,是拿到数据库的实例,然后把传进来的值也就是需要修改的值set进去,那么没有set的值即为不需要修改的值。

index.jsp

<!-- 模拟修改操作
        1. 原始数据 : 1, yexx, 123456, yexx@hust.com, 12
        2. 密码不能被修改
        3. 表单回显, 模拟操作直接在表单填写对应的属性值
     -->
    <form action="springmvc/testModelAttributes" method="post">
        <input type="hidden" name="id" value="1"/>
        username: <input type="text" name="username" value="yexx"/>
        <br/>
        email: <input type="text" name="email" value="yexx@hust.com"/>
        <br/>
        age: <input type="text" name="age" value="13"/>
        <br/>
        <input type="submit" value="Submit"/>
    </form>
package com.hust.springmvc1;

import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.hust.springmvc.entities.User;

//@SessionAttributes(value={"user"}, types={String.class})
@Controller
@RequestMapping("/springmvc")
public class SpringMVCTest { 

    private static final String SUCCESS = "success";

    /**
     * 1. 由@ModelAttribute 标记的方法,会在每个目标方法执行之前被SpringMVC调用!
     * 2. @ModelAttribute 注解也可以来修饰目标方法POJO 类型的入参,且value 属性值如下的作用:
     * 1). SpringMVC 会使用value 属性值 在implicitModel 中查找对应的对象,若存在则会直接传入到目标方法的入参中。
     * 2). SpringMVC 会一value 为 key , POJO 类型的对象为value, 存入request 中。
     */
    @ModelAttribute
    public void getUser(@RequestParam(value="id", required=false) Integer id,
            Map<String, Object> map) {
        System.out.println("ModelAttribute");
        if (id!=null) {
            //模拟从数据库中获取对象
            User user = new User(1, "yexx", "123456", "yexx@hust.com", 12);
            System.out.println("从数据库中获取一个对象:" + user);
            map.put("user", user);
        }
    }

    /**
     * 运行流程:
     * 1. 执行@ModelAttribute 注解修饰的方法:从数据库中取出对象,把对象放入到Map中。键为:user
     * 2. SpringMVC 从 Map 中取出user对象, 并把表单的请求参数赋给该User 对象对应的属性。
     * 3. SpringMVC 把上述对象传入目标方法的参数。
     * 
     * 注意: 在@ModelAttribute  修饰的方法中,放入到Map时的键需要和目标方法入参类型的第一个字母小写的字符串一致
     * 
     * SpringMVC 确定目标方法POJO 类型入参的过程
     * 1. 确定一个key:
     * 1). 若目标方法的POJO类型的参数木有使用@ModelAttribute 作为修饰,则key 为 POJO类名第一个字母的小写
     * 2). 若使用了@ModelAttribute 来修饰, 则key 为@ModelAttribute 注解的value属性值。
     * 2. 在 implicitModel 中查找 key 对应的对象, 若存在, 则作为入参传入
     * 1). 若在@ModelAttribute 标记的方法中在 Map 中保存过, 且 key 和 1 确定的key 一致,则会获取到。
     * 3. 若 implicitModel 中不存在key 对应的对象, 则检查当前的 Handler 是否使用了@SessionAttributes 注解修饰,
     * 若使用了该注解, 且 @SessionAttributes 注解的 value 属性值中包含了key, 则会从HttpSession 中来获取 key 所对应
     * 的value 的值, 若存在则直接传入到目标方法的入参中, 若不存在则将抛出异常。
     * 4. 若Handler 没有表示@SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 则会
     * 通过反射来创建 POJO类型的参数, 传入为目标方法的参数
     * 5. SpringMVC 会把key 和 POJO 类型的对象 保存到implicitModel 中, 进而会保存到 request 中。
     * 
     * 源代码分析的流程
     * 1. 调用@ModelAttribute 注解修饰的方法。 实际上把@ModelAttribute 方法中Map 中的数据放在了 implicitModel中。
     * 2. 解析请求处理器的目标参数,实际上该目标参数来自于 WebDataBinder 对象的 target属性
     * 1). 创建WebDataBinder 对象
     * ① 确定objectName 属性: 若传入的attrName 属性值为空,则objectName 为类名第一个字母小写。
     * *注意: attrName 若目标方法的POJO属性使用了@ModelAttribute 来修饰,则attrName 值即为 @ModelAttribute的value值
     * ② 确定target 属性:
     *  > 在implicitModel 中查找 attrName 对应的属性值。 若存在。 OK
     *  > 若不存在: 则验证当前Handler 是否使用了@sessionAttributes 进行修饰,若使用了,则尝试从Session中获取attrName 所对应
     *  的属性值。 若session 中没有对应的属性值,则抛出异常
     *  > 若Handler 没有使用@SessionAttributes 进行修饰, 或@SessionAttribute 中没有使用 value 值制定的 key
     *  和 attrName 相匹配,则通过反射创建POJO对象
     *  
     *  2). SpringMVC 把表单的请求参数赋给了WebDataBinder 的target 属性
     *  3). *SpringMVC 会把WebDataBinder 的attrName 和 target 给到 implicitModel
     *  4). 把WebDataBinder 的 target 作为参数传递给目标方法入参。 
     */
    @RequestMapping("/testModelAttributes")
    public String testModelAttributes(@ModelAttribute("user") User user) {
        System.out.println("update:" + user);
        return SUCCESS;
    }   
}

这段程序的运行结果就会是:
这里写图片描述

该回显数据也是能够显示出来。我把执行过程和源码分析拿出来特别的说一下。

运行流程:

 * 1. 执行@ModelAttribute 注解修饰的方法:从数据库中取出对象,把对象放入到Map中。键为:user
 * 2. SpringMVC 从 Map 中取出user对象, 并把表单的请求参数赋给该User 对象对应的属性。
 * 3. SpringMVC 把上述对象传入目标方法的参数。
 * 
 * 注意: 在@ModelAttribute  修饰的方法中,放入到Map时的键需要和目标方法入参类型的第一个字母小写的字符串一致。

源代码分析的流程

 * 1. 调用@ModelAttribute 注解修饰的方法。 实际上把@ModelAttribute 方法中Map 中的数据放在了 implicitModel中。
 * 2. 解析请求处理器的目标参数,实际上该目标参数来自于 WebDataBinder 对象的 target属性
 * 1). 创建WebDataBinder 对象
 * ① 确定objectName 属性: 若传入的attrName 属性值为空,则objectName 为类名第一个字母小写。
 * *注意: attrName 若目标方法的POJO属性使用了@ModelAttribute 来修饰,则attrName 值即为 @ModelAttribute的value值
 * ② 确定target 属性:
 *  > 在implicitModel 中查找 attrName 对应的属性值。 若存在。 OK
 *  > 若不存在: 则验证当前Handler 是否使用了@sessionAttributes 进行修饰,若使用了,则尝试从Session中获取attrName 所对应
 *  的属性值。 若session 中没有对应的属性值,则抛出异常
 *  > 若Handler 没有使用@SessionAttributes 进行修饰, 或@SessionAttribute 中没有使用 value 值制定的 key
 *  和 attrName 相匹配,则通过反射创建POJO对象
 *  
 *  2). SpringMVC 把表单的请求参数赋给了WebDataBinder 的target 属性
 *  3). *SpringMVC 会把WebDataBinder 的attrName 和 target 给到 implicitModel
 *  4). 把WebDataBinder 的 target 作为参数传递给目标方法入参。 

SpringMVC 确定目标方法POJO 类型入参的过程

 * 1. 确定一个key:
 * 1). 若目标方法的POJO类型的参数木有使用@ModelAttribute 作为修饰,则key 为 POJO类名第一个字母的小写
 * 2). 若使用了@ModelAttribute 来修饰, 则key 为@ModelAttribute 注解的value属性值。
 * 2. 在 implicitModel 中查找 key 对应的对象, 若存在, 则作为入参传入
 * 1). 若在@ModelAttribute 标记的方法中在 Map 中保存过, 且 key 和 1 确定的key 一致,则会获取到。
 * 3. 若 implicitModel 中不存在key 对应的对象, 则检查当前的 Handler 是否使用了@SessionAttributes 注解修饰,
 * 若使用了该注解, 且 @SessionAttributes 注解的 value 属性值中包含了key, 则会从HttpSession 中来获取 key 所对应
 * 的value 的值, 若存在则直接传入到目标方法的入参中, 若不存在则将抛出异常。
 * 4. 若Handler 没有表示@SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 则会
 * 通过反射来创建 POJO类型的参数, 传入为目标方法的参数
 * 5. SpringMVC 会把key 和 POJO 类型的对象 保存到implicitModel 中, 进而会保存到 request 中。

总结

* 1. 由@ModelAttribute 标记的方法,会在每个目标方法执行之前被SpringMVC调用!
 * 2. @ModelAttribute 注解也可以来修饰目标方法POJO 类型的入参,且value 属性值如下的作用:
 * 1). SpringMVC 会使用value 属性值 在implicitModel 中查找对应的对象,若存在则会直接传入到目标方法的入参中。
 * 2). SpringMVC 会一value 为 key , POJO 类型的对象为value, 存入request 中。

这里有一个特别值得注意的地方

这里写图片描述
如果你的Controller被@SessionAttributes修饰了,而且value也是那个,而且没用@ModelAttribute修饰方法,同时也没有@ModelAttribute修饰目标方法入参。这个时候就会抛出异常。我们知道原理之后很容易去避免这个异常。

这里写图片描述

目录
相关文章
|
2月前
|
存储 Java API
如何使用 Java 记录简化 Spring Data 中的数据实体
如何使用 Java 记录简化 Spring Data 中的数据实体
38 9
|
2月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
2月前
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
30 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
|
2月前
|
JSON 前端开发 Java
【Spring】“请求“ 之传递 JSON 数据
【Spring】“请求“ 之传递 JSON 数据
87 2
|
2月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
60 2
|
3月前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
2月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
158 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
3月前
|
XML 缓存 前端开发
springMVC02,restful风格,请求转发和重定向
文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
springMVC02,restful风格,请求转发和重定向
|
2月前
|
前端开发 Java 数据库
springBoot:template engine&自定义一个mvc&后端给前端传数据&增删改查 (三)
本文介绍了如何自定义一个 MVC 框架,包括后端向前端传递数据、前后端代理配置、实现增删改查功能以及分页查询。详细展示了代码示例,从配置文件到控制器、服务层和数据访问层的实现,帮助开发者快速理解和应用。
|
4月前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】