Spring MVC @ModelAttribute注解


  在一個Controller內,被@ModelAttribute標注的方法會在此controller的每個handler方法執行前被執行。

  被@ModelAttribute標注的方法的參數綁定規則和普通handler方法相同。

  可以理解為:

  1. 請求到達Controller后,不論其他handler方法的RequestMapping值是多少,請求都會路由至被@ModelAttribute標注的方法;
  2. 由springMVC再對request執行一次forward,路由至真正的handler方法。

一 @ModelAttribute用於注解方法

1 方法返回類型為void

  這種情況,@ModelAttribute只是單純的作為請求路由的第一站,使用者可在方法內部操作Model和Request等參數實現功能。

   對於如下請求:

http://localhost:8080/TestModelAttributeController/testHandler.action?reqParam=123

  對應的Controller:

@Controller
@RequestMapping("/TestModelAttributeController")
public class TestModelAttributeController {

    @ModelAttribute
    public void modelAttributeMethod(HttpServletRequest request, String reqParam, Model model){
        model.addAttribute("reqParam",reqParam);
        request.setAttribute("methodParam","Hello ModelAttribute");
    }


    @RequestMapping("/testHandler")
    public String testHandler(){
        return "testModelAttribute";
    }
}

   testModelAttribute.jsp如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>${reqParam}</h1>
<h1>${methodParam}</h1>
</body>
</html>

  最終可以在頁面中看到:

  123

  Hello ModelAttribute

2 方法返回類型不為void

這種情況,@ModelAttribute會將返回值放到Model中,並將該類型名稱的首字母小寫作為model中的屬性名

請求地址和參數不變。

對應的Controller:

    @ModelAttribute
    public User userModelAttributeMethod2(){
        User user = new User();
        user.setAge(31);
        user.setName("James");
        user.setEmail("123456@qq.com");
        return user;  //相當於model.addAttribute("user",user);
    }


    @RequestMapping("/testHandler")
    public String testHandler(Model model){
        System.out.println(model.containsAttribute("user"));  //true
        return "testModelAttribute";
}

 對應的jsp頁面

<h1>${user.age}</h1>
<h1>${user.email}</h1>
<h1>${user.name}</h1>

   實際上,對於返回類型為void的方法,@ModelAttribute也會在model中添加一對鍵值對,“void”->"null"

3 方法返回類型不為void,且@ModelAttribute指定屬性名稱

  這種情況下,@ModelAttribute會將返回值放到Medel中,且對應的key值為@ModelAttribute置頂的屬性名

   對應的Controller:

 @ModelAttribute("myUser")
    public User userModelAttributeMethod2(){
        User user = new User();
        user.setAge(31);
        user.setName("James");
        user.setEmail("123456@qq.com");
        return user;  //相當於model.addAttribute("user",user);
    }


    @RequestMapping("/testHandler")
    public String testHandler(Model model){
        System.out.println(model.containsAttribute("user"));  //true
        return "testModelAttribute";
}

  對應的jsp頁面:

<h1>${myUser.age}</h1>
<h1>${myUser.email}</h1>
<h1>${myUser.name}</h1>

 

4 @ModelAttribute和@RequestMapping注解同一個方法

  這種情況下:

  1. 在controller處理其他請求時,不會再首先進入被@ModelAttribute和@RequestMapping同時注解的方法
  2. 該方法的返回值不再是視圖的邏輯名稱,而是按照@ModelAttribute的規則被加入到Model中;
  3. @RequestMapping注解的value值具有兩個作用
    • 作為URI,實現請求的路由;
    • 作為此次請求的邏輯視圖名(嚴格來說此時視圖的邏輯視圖名是:controller的RequestMapping值+method的RequestMapping值
@Controller
@RequestMapping("/TestModelAttributeController")
public class TestModelAttributeController {

    @RequestMapping("/testModelAttribute")
    @ModelAttribute("result")
    public String testModelAttribute(){
        return "excellent";
    }

 

<body>
<h1>${result}</h1>
</body>

 

如上Controller和jsp:

testModelAttribute方法的作用是:

  1. 處理路徑為 /TestModelAttributeController/testModelAttribute*的請求
  2. 將鍵值對"result"->"excellent"放至model中,為視圖渲染提供數據
  3. 返回邏輯視圖名 /TestModelAttributeController/testModelAttribute

二 @ModelAttribute注解方法入參

@ModelAttribute("attrName")用在方法入參上時,作用為:

  • 從當前的隱式model對象中取key值attrName所對應的attrValue值,並將attrValue賦給被注解的參數。
  • 而且自動暴露為模型數據用於視圖頁面展示時使用

1 @ModelAttribute指定注解的value值attrName

  如下所示,myUser和newParam兩個model屬性對應的attrValue值,將被賦值給方法入參。

    @ModelAttribute("myUser")
    public User userModelAttributeMethod2(Model model){
        User user = new User();
        user.setAge(31);
        user.setName("James");
        user.setEmail("123456@qq.com");
        
        model.addAttribute("newParam","new parameter");
        return user;
    }


    @RequestMapping("/testHandler")
    public String testHandler(@ModelAttribute("myUser") User user,@ModelAttribute("newParam") String newParam){
        System.out.println(user);
        System.out.println(newParam);
        return "testModelAttribute";
    

2 @ModelAttribute注解value值缺省

  這時默認的attrName為類型名稱的首字母小寫

  如下例,user能夠從model中獲取,但是 newParam從model中獲取的值為null

    @ModelAttribute("user") //此處必須是 user
    public User userModelAttributeMethod2(Model model){
        User user = new User();
        user.setAge(31);
        user.setName("James");
        user.setEmail("123456@qq.com");

        model.addAttribute("newParam","new parameter");
        return user;
    }


    @RequestMapping("/testHandler")
    public String testHandler(@ModelAttribute User user,@ModelAttribute String newParam){
        System.out.println(user);
        System.out.println(newParam);
        return "testModelAttribute";
    }

 

3 入參不使用@ModelAttribute注解

  這種情況下,簡單類型參數不會從model中取值,簡單類型定義由 org.springframework.beans.BeanUtils#isSimpleValueType指定,如下:

    /**
     * Check if the given type represents a "simple" value type:
     * a primitive, an enum, a String or other CharSequence, a Number, a Date,
     * a URI, a URL, a Locale or a Class.
     * @param clazz the type to check
     * @return whether the given type represents a "simple" value type
     */
    public static boolean isSimpleValueType(Class<?> clazz) {
        return (ClassUtils.isPrimitiveOrWrapper(clazz) ||
                Enum.class.isAssignableFrom(clazz) ||
                CharSequence.class.isAssignableFrom(clazz) ||
                Number.class.isAssignableFrom(clazz) ||
                Date.class.isAssignableFrom(clazz) ||
                URI.class == clazz || URL.class == clazz ||
                Locale.class == clazz || Class.class == clazz);
    }
View Code

 

  非簡單類型會從model中取值,這時默認的attrName為類型名稱的首字母小寫。

  如下例,user能夠從model中獲取,但是newParam是簡單類型,所以不會從model中取值。

 4 使用request參數和@ModelAttribute同時為同一個入參賦值

這種情況需要根據入參的類型區別對待

  1. 對於非簡單類型,spring會先使用@ModelAttribute為參數賦值,然后使用request的參數對入參的屬性值進行覆蓋;
  2. 對於簡單類型,spring會使用@ModelAttribute為參數賦值,忽略request參數;

如下例所示,URL為:

這時user的name和age屬性最終會被設置為request對應的參數值(也就是實現了對象合並),而reqParam的值最終會采用@ModelAttribute得到的值

    @ModelAttribute("user") //此處必須是 user
    public User userModelAttributeMethod2(Model model){
        User user = new User();
        user.setAge(31);
        user.setName("James");
        user.setEmail("123456@qq.com");

        model.addAttribute("reqParam","from @ModelAttribute");
        return user;
    }


    @RequestMapping("/testHandler")
    public String testHandler( @ModelAttribute("user") User user, @ModelAttribute("reqParam") String reqParam){
        System.out.println(user);  //user 對象的屬性會被request的參數覆蓋
        System.out.println(reqParam);  //reqParam參數的值始終是@ModelAttribute中的值,不會被覆蓋
        return "testModelAttribute";
    }

 

三 @ModelAttribute注解方法返回值

  此時@ModelAttribute的作用是將返回值添加至model,而這時的邏輯視圖名稱為:controller的RequestMapping值+method的RequestMapping值

  其實,這種用法和 @ModelAttribute和@RequestMapping注解同一個方法相同

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM