hualinux springMVC 5.8:@ModelAtrribute注解

    技术2023-05-30  89

    目录

    一、@ModelAttribute注解详解之使用场景

    1.1 常用场景分析

    1.2 有问题的做法

    1.2.1 传统做法

    1.2.2 传统做法实现代码

    1.3 正确做法

    1.3.1 正确做法图示

    1.3.2 实现代码

    1.4 @ModelAtrribute注解的执行流程

    二、 @ModelAtrribute源代码分析

    三、 springMVC 确定目标方法 POJO 类型入参的过程

    四、解决@ModelAtrribute中,map的键与执行目标方法的参数名不一致


    @ModelAttribute注释方法 , 在Spring mvc中,注解@ModelAttribute是一个非常常用的注解,其功能主要在两方面:

    1、运用在参数上,会将客户端传递过来的参数按名称注入到指定对象中,并且会将这个对象自动加入ModelMap中,便于View层使用;

    2、运用在方法上,会在每一个@RequestMapping标注的方法前执行,如果有返回值,则自动将该返回值加入到ModelMap中;

    @ModelAttribute等价于上一章的 model.addAttribute("attributeName", abc); 

     

    一、@ModelAttribute注解详解之使用场景

    1.1 常用场景分析

    表有几个字段,只修改符合条件的一条记录中的字段,其它一个字段不能修改。如密码不能修改,id是自增的

    1.2 有问题的做法

    1.2.1 传统做法

    传统一般做法,如图:

    new一个对象,然后表单参数传给这个对象,再执行update命令更新整个对象,其中两个字段有值,但会出现一个问题,其中一个字段没斌值,没有被更新的字段值会为空!

     

    1.2.2 传统做法实现代码

    先建立相关代码 src/main/java/com.hualinux.springmvc.entities/User2.java代码

    package com.hualinux.srpingmvc.entities; public class User2 { private Integer id; private String username; private String password; private String email; private int age; public User2() { } public User2(Integer id, String username, String password, String email, int age) { this.id = id; this.username = username; this.password = password; this.email = email; this.age = age; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User2{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", email='" + email + '\'' + ", age=" + age + '}'; } }

    在src/main/java/com.hualinux.srpingmvc.handers/SpringMVCTest.java,添加如下代码

    先把 @SessionAttributes注解掉

    @RequestMapping("/springmvc/testModelAtrribute2") public String testModelAtrribute2(User2 user2){ System.out.println("修改:"+user2); return "ok"; }

    打开web/index.jsp,在其页面的<body></body>标签中添加如下代码:

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

    运行tomcat,idea会自动打开主页点,输入如下内容:

    看到返回“ok,访问成功!”时,再看idea最下方的日志输出如下:

    修改:User2{id=1, username='Tom', password='null', email='tom@hualinux.com', age=12}

    上面发现密码是空,原来是123456的,你现在把它变成空了,肯定不行,不符合需求!

     

    1.3 正确做法

    1.3.1 正确做法图示

     

    1.3.2 实现代码

    在src/main/java/com.hualinux.srpingmvc.handers/SpringMVCTest.java,添加多一段代码,如下:

    //有@ModelAtrribute注解的方法,会在每个目标方法执行前被springMVC调用 @ModelAttribute public void getUser(@RequestParam(value = "id",required = false) Integer id, Map<String,Object> map ){ if (id != null){ //模拟从数据库中获取对象,这里为了方便就没有设置连接数据库 User2 user2 = new User2(1,"li","123456","li@hualinux.com",20); System.out.println("从数据库中获取一个对象:"+user2); //把信息放在map中, map.put("user2",user2); } }

    注意:在@ModelAttribute 修饰方法中,放入到Map时的键需要和目标方法入参参数的第一个字母小字的字符串一致。

    这样总的代码有2段,如下:

     

    再次运行一次看一下效果

    看到返回“ok,访问成功!”时,再看idea最下方的日志输出如下:

    发现名字、email、年龄都被修改了,密码没变,正是我们想要的效果

    1.4 @ModelAtrribute注解的执行流程

    1.执行@ModelAtrribute注解修饰的方法:从数据库中取出对象,并把对象放到了Map中,键为user

    2.springMVC从Map集合中取出User对象,并把表单的请求参数赋值给user对象相应的属性

    3.springMVC把上述对象传入目标方法的参数

    4.这个user对象是存在request中,如果jsp表单中有对应的字段,还会自动填充表单

    注意:在@ModelAtrribute修饰的方法中,放入Map时的键要和目标方法的参数名一致

     

    二、 @ModelAtrribute源代码分析

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

     

    三、 springMVC 确定目标方法 POJO 类型入参的过程

    SpringMVC 确定目标方法P0J0类型入参的过程 1. 确定一个key:   1)若目标方法的P0J0类型的参数木有使用@ModelAttribute作为修饰,则key为P0J0类名第一个字母的小写   2)若使用了@ModelAttribute 来修饰,则key为@ModelAttribute注解的value属性值。

    2.在implicitModel 中查找key对应的对象,若存在,则作为入参传入。 若在@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和P0J0类型的对象保存到implicitModel中,进而会保存到request中。

     

    四、解决@ModelAtrribute中,map的键与执行目标方法的参数名不一致

    解决@ModelAtrribute注解中,map集合的键与执行目标方法的参数名不一致的情况

    其实我们可以在目标方法里面的参数中,定义一个@ModelAtrribute注解,并把其值指定为@ModelAtrribute注解修饰的方法中的map的键

    src/main/java/com.hualinux.srpingmvc.handers/SpringMVCTest.java,相关的代码进行了一下修改

    运行tomcat,idea会自动打开主页点,输入如下内容:

    看到返回“ok,访问成功!”时,再看idea最下方的日志输出如下:

    修改后的结果和没修改前的结果是一样的。

     

    Processed: 0.015, SQL: 9