天天看点

Springmvc知识二细节[email protected]举例说明分析流程使用总结

举例说明

对于@modelattribute注解比较吃力。现在我们先考虑一个问题,关于数据的修改。

假设我们需要修改一个User对象,但是我们规定某一个或者几个字段不能修改,我们会利用表单填写相关信息,然后提交给后台,然后 在后台会new出一个对象,并将表单提交的数据赋值到这个表单的属性中,然后执行update操作,但是由于有些字段不能被修改,所以我们在前台传递的对象有些属性为空,所以在更新的时候,那些不能被修改的字段就会被默认为空值。

Springmvc知识二细节[email protected]举例说明分析流程使用总结

解决:1、我们可以利用隐藏域,在用户进行修改的时候,将对应的不能修改的字段进行隐藏,这样的话,可以解决,但是如果对应的字段的敏感度比较高,例如密码不适合利用隐藏域进行记录,这种方法是不合适的。

2、我们可以在数据更新之前,先从数据库中将用户信息给读取出来,然后将不能修改的字段重新进行赋值然后更新。但是如果所不能修改的字段比较多,我们就会对这个比较麻烦。重复代码较多。

引出:

对于上述两种方式都有对应的缺点,但是对于更新数据比较小的可以使用,所以我们可以利用我们的这个ModelAttribute注解进行处理。

Springmvc知识二细节[email protected]举例说明分析流程使用总结

注意 这里的对象不是new出来的,而是从数据库获取出来的,这就是ModelAttribute的作用之一。

在没有利用@ModelAttribute修饰前演示

<!-- POJO类ModelAttributes属性  -->
     private int id;  
     private String name;
     private int age;
     private String email;
     private String password; // 不能被修改


<!--
            模拟修改操作  前台页面
            1.原始数据为 1 tom  12 [email protected] 12345
            2.密码不能修改
            3.表单回显 ,模拟操作直接在表单填写对应的属性值           
            在用户修改数据时,有些数据不能修改,所以在修改时我们把这些子段不给显示,
            我们应该是从数据库中先获取,然后 利用ModelAttribute注解进行 覆盖,
            会比我们自己手动利用覆盖的效果高
 -->

<form action="TestModelArributes" method="post" >
    <input type="hidden" name="id" value="1">
    name:<input type="text" name="name" value="tom">
    age:<input type="text" name="age" value="12" >
    email:<input type="text" name="eamil" value="[email protected]" >
    <input type="submit" value="submit"> 
</form>

<!-- 后台处理 -->
@RequestMapping("/TestModelArributes")
    public String TestModelArributes(User user){
        System.out.println("修改:"+user);
        // 执行更新操作 省略 
        return SUCCESS;
    }
<!-- 控制台打印结果 -->
修改:User[id=1, name=tom, age=13, [email protected], password=null]
           

注意:在这里我们的原始数据 带有密码,但是从前台到后台处理的时候,这个密码由于没有在表单之中,所以传过来的POJO 的密码为空。即利用方法一所造成的。

然后我们在后台处理中新增加一个方法,注意是被@ModelAttribute修饰的。

@ModelAttribute
    public void getUser(@RequestParam(value="id",required=false) Integer id,
            Map<String, Object> maps){
        if(id != null){   // id不为空,说明是用户执行了修改操作
            // 模拟从数据库中获取对象 我们是生成的
        User user =new User(,"tom", ,"[email protected]", "1234");
        System.out.println("从数据库中获取一个对象:"+user );
        maps.put("user", user);
        }   
    }

<!-- 控制台打印结果 -->

从数据库中获取一个对象:User[id=, name=tom, age=, [email protected], password=]
修改:User[id=, name=tom, age=, [email protected], password=]
           

当被@ModelAttribute修饰一个方法之后,结果就变成了password有值了,而且也应该注意到,被@ModelAttribute修饰的方法给提前执行了,这就是@ModelAttribute的作用。这样的话,再做修改操作的话,就能将原始密码给带进入修改操作了。为什么被@ModelAttribute修饰一个方法之后就会有这样的结果呢?

下面我们抽出主要代码进行分析

<!-- 第一种方式 -->
@ModelAttribute
    public void getUser(@RequestParam(value="id",required=false) Integer id,
            Map<String, Object> maps){
        if(id != null){
        User user =new User(,"tom", ,"[email protected]", "1234");
        maps.put("user", user);
        }   
    }

    @RequestMapping("/TestModelArributes")
    public String TestModelArributes(User user){
        System.out.println("修改:"+user);
        return SUCCESS;
    }

=============================


    <!--第二种方式 -->
    @ModelAttribute
    public void getUser(@RequestParam(value="id",required=false) Integer id,
            Map<String, Object> maps){
        if(id != null){
        User user=new User(,"tom", , "1234");
        maps.put("change", user);
        }


        @RequestMapping("/TestModelArributes")
        public String TestModelArributes(@ModelAttribute( value="change") User user){
            System.out.println("修改:"+user);
            return SUCCESS;
        }
           

注意

1、@ModelAttribute修饰的方法会被Springmvc提前执行,运行在每个目标方法之前。

2、在@ModelAttribute修饰的方法中,【map存入的键需要和目标方法入参类型的第一个字母小写的字符串一致】方式一所示,否则的话,在目标的请求参数方法中不能匹配到从数据库中提取出来的数据,或者就是你也将目标方法的参数用利用注解ModelAttribute修饰且设置value属性值指向你的map中的键名,上述代码的第二种方式

分析流程

运行流程:

我们上述代码可以分为三个步骤:

1、 执行ModelArribute修饰的方法,从数据库中取出对象,把对象放到Map 中,键为user。

2、springmvc 从Map 中取出User 对象 ,并把表单的请求参数赋给该User对象的对应属性。

3、springmvc 把上述对象传入到目标方法的参数。

@ModelAttribute源码分析流程

源码分析流程:对应上面的运行流程。

1、调用@ModelAttribute 注解修饰的方法,实际上把@ModelAttribute方法中 Map中的数据放在了implicitModel中。

2、解析请求处理器的目标参数,实际上该目标参数来自于WebDataBinder对象的target属性。

2.1、创建WebDataBinder对象。

a) 确定objectName属性:若传入的attrName属性值为空”“,则objectName为类名第一个字母小写。需要注意的是:attrName。如果目标方法的POJO参数属性使用了@ModelAttribute来修饰,则attrName值即为@ModelAttribute的value属性值(对应我们的第二种写法)

b)确定target属性:在implicitModel 中查找attrName对应的属性值,如果存在,进行利用。如果不存在,则验证当前Handler是否使用了@SessionAttribute进行修饰类。如果使用了@SessionAttribute,则尝试从HttpSession中获取attrName所对应的值,若Session中没有所对应的值,则会抛出异常。【这也是SessionAttribute与ModelAttribute共同使用时造成的一个常见问题】,如果当前Handler没有使用@SessionAttribute修饰类,且@SessionAttribute中没有使用value值指定的key和attrName相匹配,则通过反射机制进行创建POJO 对象。

2.2、Springmvc把表单的请求参数赋值给WebDataBinder 的target对应的属性。

2.3、Springmvc 会把WebDataBinder的attrName和target给到implicitModel,进而传递到request域对象中。

3、把WebDataBinder的target作为参数传递给目标方法。

POJO入参流程

从而我们也可以得出Springmvc进行传递POJO类型作为参数的流程:

1、确定一个key:

1.1、若目标方法的POJO类型的参数没有使用@ModelAttribute 作为修饰,则key为POJO类名第一个字母的小写。

1.2、如果使用了@ModelAttribute 来修饰,则key为@ModelAttribute 注解的value属性值。

2、在implicitModel中查找key对应的对象,如果存在则作为入参传入。

2.1、如在@ModelAttribute标记的方法中保存过,且key值与上述1中key一致,也可以进行获取(这就是上述代码为什么可以获取@ModelAttribute修饰的方法中map中的值)

3、若implicitModel 中不存在key对应的对象,则检查当前Handler是否使用了@SessionAttribute进行修饰类,如果使用了@SessionAttribute,且@SessionAttribute注解的value属性值中包含了key,则会从HttpSession中来获取key所对应的value值,若不存在则会抛出异常。

4、如果当前Handler没有使用@SessionAttribute修饰类,且@SessionAttribute中value属性值中不包含key,则通过反射机制进行创建POJO 对象。作为目标的方法参数对象。

5、Springmvc会把key和pojo类型的对象报错到implicitModel 中,进而保存到Request中。

使用总结

1、注解在没有返回值的方法上面

就如上面的例子注解在没有返回值的方法上面,用在POJO入参上面,或者我们可以在这里设定一些Request域中的对象,在前台可以直接使用。

@ModelAttribute
    public void getUser(){
            maps.put("name", "zhangsan");
        }
           

2、注解在有返回值的方法上面,

这里要用value属性进行数据存储,会将返回值存储在value属性的值中,可以在前台直接获取。

@ModelAttribute(value="user00")
    public User testModelAttribute(){
        User user =new User(,"tom00", ,"[email protected]", "123400");
        return user;
    }
           

3、在方法参数上使用@ModelAttribute

在方法的参数中使用必须是修饰POJO类,这样的话,才能从相应的域中获取数据

@RequestMapping("/TestModelArributes")
        public String TestModelArributes(@ModelAttribute( value="change") User user){
            System.out.println("打印"+user);
            return SUCCESS;
        }
           

这样的话,会从implicitModel 隐藏域中查询此类型对象,就犹如上面分析的传递POJO流程,其实这里的作用只是可以使用change标记我们的user对象。

4、尤其要注意ModelAttribute与SessionAttribute同时使用会产生的异常抛出,以及原因。

5、一定要理解POJO传递参数的流程

对于源码分析不理解的建议去看尚硅谷的佟刚的Springmvc教程,本实例就是根据他的视频写的笔记。

继续阅读