Spring MVC @ModelAttribute

1.@ModelAttribute注释void返回值的方法

@Controller
public class HelloModelController {
    
    @ModelAttribute 
    public void populateModel(@RequestParam String abc, Model model) {  
       model.addAttribute("attributeName", abc);  
    }  

    @RequestMapping(value = "/helloWorld")  
    public String helloWorld() {  
       return "helloWorld.jsp";  
    }  

}
在这个代码中,访问控制器方法helloWorld时,会首先调用populateModel方法,将页面参数abc(/helloWorld.ht?abc=text)放到model的attributeName属性中,在视图中可以直接访问。

jsp页面页面如下:
[html] 
<c:out value="${attributeName}"></c:out>  


2.@ModelAttribute注释返回具体类的方法
@Controller  
public class Hello2ModelController {  
      
    @ModelAttribute   
    public User populateModel() {    
       User user=new User();  
       user.setAccount("ray");  
       return user;  
    }    
    @RequestMapping(value = "/helloWorld2")    
    public String helloWorld() {    
       return "helloWorld.jsp";    
    }    
}  
@Controller
public class Hello2ModelController {
    
    @ModelAttribute 
    public User populateModel() {  
       User user=new User();
       user.setAccount("ray");
       return user;
    }  
    @RequestMapping(value = "/helloWorld2")  
    public String helloWorld() {  
       return "helloWorld.jsp";  
    }  
}

当用户请求 http://localhost:8080/test/helloWorld2.html时,首先访问populateModel方法,返回User对象,model属性的名称没有指定,它由返回类型隐含表示,如这个方法返回User类型,那么这个model属性的名称是user。  
这个例子中model属性名称有返回对象类型隐含表示,model属性对象就是方法的返回值。它无须要特定的参数。

jsp 中如下访问:
[html] view plaincopyprint?
<c:out value="${user.account}"></c:out>  
<c:out value="${user.account}"></c:out>

也可以指定属性名称

[java] view plaincopyprint?
@Controller  
public class Hello2ModelController {  
      
    @ModelAttribute(value="myUser")  
    public User populateModel() {    
       User user=new User();  
       user.setAccount("ray");  
       return user;  
    }    
    @RequestMapping(value = "/helloWorld2")    
    public String helloWorld(Model map) {    
       return "helloWorld.jsp";    
    }    
}  
@Controller
public class Hello2ModelController {
    
    @ModelAttribute(value="myUser")
    public User populateModel() {  
       User user=new User();
       user.setAccount("ray");
       return user;
    }  
    @RequestMapping(value = "/helloWorld2")  
    public String helloWorld(Model map) {  
       return "helloWorld.jsp";  
    }  
}

jsp中如下访问:

[html] view plaincopyprint?
<c:out value="${myUser.account}"></c:out>  
<c:out value="${myUser.account}"></c:out>

对象合并:
[java] view plaincopyprint?
@Controller  
@Controller
public class Hello2ModelController {
    
    @ModelAttribute
    public User populateModel() {  
       User user=new User();
       user.setAccount("ray");
       return user;
    }  
    
    @RequestMapping(value = "/helloWorld2")  
    public String helloWorld(User user) {
        user.setName("老王");
       return "helloWorld.jsp";  
    }  
}
 
对象合并指定对象名称:
[java] 
@Controller
public class Hello2ModelController {
    
    @ModelAttribute("myUser")
    public User populateModel() {  
       User user=new User();
       user.setAccount("ray");
       return user;
    }  
    
    @RequestMapping(value = "/helloWorld2")  
    public String helloWorld(@ModelAttribute("myUser") User user) {
        user.setName("老王");
       return "helloWorld.jsp";  
    }  
}
这样在jsp中可以使用如下方式访问
[html] 
<c:out value="${myUser.name}"></c:out>  
<c:out value="${myUser.account}"></c:out>  
<c:out value="${myUser.name}"></c:out>
<c:out value="${myUser.account}"></c:out>

3.通过此特性控制权限.
我们可以在基类方法中控制写此注解,需要控制权限的控制器,继承控制器就可以了。
public class BaseController {  
      
    @ModelAttribute  
    public void populateModel() throws Exception {    
       SysUser user=ContextUtil.getCurrentUser();  
       if(user.getAccount().equals("admin")){  
           throw new Exception("没有权限");  
       }  
    }    
}  
需要控制权限的类继承BaseController
@Controller  
public class Hello2ModelController extends BaseController {  
      
    @RequestMapping(value = "/helloWorld2")    
    public String helloWorld(@ModelAttribute("myUser") User user) {  
        user.setName("老王");  
       return "helloWorld.jsp";    
    }    
}  
这样就可以控制权限了,当然控制权限的方法有很多,比如通过过滤器等。这里只是提供一种思路。

总结:

@ModelAttribute具有如下三个作用:
①绑定请求参数到命令对象:放在功能处理方法的入参上时,用于将多个请求参数绑定到一个命令对象,从而简化绑定流程,而且自动暴露为模型数据用于视图页面展示时使用。其实@ModelAttribute此处对于供视图页面展示来说与model.addAttribute("attributeName", abc);功能类似。
[java] view plaincopyprint?
public String test(@ModelAttribute("user") UserModel user)   

此处多了一个注解@ModelAttribute("user"),它的作用是将该绑定的命令对象以“user”为名称添加到模型对象中供视图页面展示使用。我们此时可以在视图页面使用${user.username}来获取绑定的命令对象的属性。

②暴露@RequestMapping 方法返回值为模型数据:放在功能处理方法的返回值上时,是暴露功能处理方法的返回值为模型数据,用于视图页面展示时使用。

public @ModelAttribute("user2") UserModel test3(@ModelAttribute("user2") UserModel user)  

大家可以看到返回值类型是命令对象类型,而且通过@ModelAttribute("user2")注解,此时会暴露返回值到模型数据( 名字为user2 ) 中供视图展示使用
@ModelAttribute 注解的返回值会覆盖@RequestMapping 注解方法中的@ModelAttribute 注解的同名命令对象


③暴露表单引用对象为模型数据:放在处理器的一般方法(非功能处理方法)上时,是为表单准备要展示的表单引用对象,如注册时需要选择的所在城市等,而且在执行功能处理方法(@RequestMapping 注解的方法)之前,自动添加到模型对象中,用于视图页面展示时使用;

需求:

     要求修改数据库中的某条记录的部分,部分值不能给修改.例如:数据库表有3个字段,1个字段不能被修改,而更新的时候,表单提交的只有两个参数,实体中只有两个属性被赋值,其中一个属性是null.用平常的update更新,会有一个字段被置空.一般的更新方法如下图所示:


传统解决方案:

     一.隐藏域但是隐藏域有两个问题,问题一:如果是比较敏感的字段,不能放隐藏域,另一个问题就是字段较多的情况,使用隐藏域比较麻烦.

     二.更新之前,先做查询操作.把表单未提交的值通过查询获取.这样的方法也比较麻烦.

 

高质量解决方案:使用SpringMVC注解@ModelAttribute

对象从数据库中获取的.如图所示:


出现这样的原因是对象是new出来的,建立一个新的对象,这个对象之是空的.

具体代码:

Hander:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1.   /** 
  2.  *  有 @ModelAttribute 标记的方法, 会在每个目标方法执行之前被 SpringMVC 调用!  
  3.  */  
  4. @ModelAttribute  
  5. public void getUser(@RequestParam(value="id",required=false) Integer id,   
  6.         Map<String, Object> map){  
  7.     System.out.println("modelAttribute method");  
  8.     if(id != null){  
  9.         //模拟从数据库中获取对象  
  10.         User user = new User(1"Tom""123456""tom@atguigu.com"12);  
  11.         System.out.println("从数据库中获取一个对象: " + user);  
  12.           
  13.         map.put("user", user);  
  14.     }  
  15. }  
[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. @RequestMapping("/testModelAttribute")  
  2. public String testModelAttribute(User user){  
  3.     System.out.println("修改: " + user);  
  4.     return SUCCESS;  
  5. }  

View:

[java]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. <!--    
  2.         模拟修改操作  
  3.         1. 原始数据为: 1, Tom, 123456,tom@atguigu.com,12  
  4.         2. 密码不能被修改.  
  5.         3. 表单回显, 模拟操作直接在表单填写对应的属性值  
  6.     -->  
  7.     <form action="springmvc/testModelAttribute" method="Post">  
  8.         <input type="hidden" name="id" value="1"/>  
  9.         username: <input type="text" name="username" value="Tom"/>  
  10.         <br>  
  11.         email: <input type="text" name="email" value="tom@atguigu.com"/>  
  12.         <br>  
  13.         age: <input type="text" name="age" value="12"/>  
  14.         <br>  
  15.         <input type="submit" value="Submit"/>  
  16.     </form>  

执行结果:



 运行流程:

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

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

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

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


下面在dug的模式下,通过分析源代码来分析流程:

      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进行修饰,@SessionAttributes中没有使用 value值指定的 key attrName相匹配,则通过反射创建了 POJO对象

 

           2). SpringMVC把表单的请求参数赋给了 WebDataBinder target对应的属性.

           3). SpringMVC会把 WebDataBinder attrName target给到 implicitModel.近而传到 request 域对象中.

           4). WebDataBinder target作为参数传递给目标方法的入参


pojo类型的参数:

()如何确定

  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.

 

()修饰入差

      2. @ModelAttribute 注解也可以来修饰目标方法 POJO类型的入参, value属性值有如下的作用:

           1). SpringMVC会使用 value属性值在 implicitModel中查找对应的对象,若存在则会直接传入到目标方法的入参中.

            2). SpringMVC会一 value key, POJO类型的对象为 value,存入到 request


     巧妙的使用@ModelAttribute属性解决更新有的值被置空的问题.学会站在巨人的肩膀上,提高自己的代码质量.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值