springMVC對簡單對象,Set,List,Map的數據綁定及類型轉換

一,數據綁定基本定義:

引用百度的數據綁定定義:
    簡單綁定是將一個用戶界面元素(控件)的屬性綁定到一個類型(對象)實例上的某個屬性的方法。例如,若是一個開發者有一個Customer類型的實例,那麼他就能夠把Customer的「Name」屬性綁定到一個TextBox的「Text」屬性上。「綁定」了這2個屬性以後,對TextBox的Text屬性的更改將「傳播」到Customer的Name屬性,而對Customer的Name屬性的更改一樣會「傳播」到TextBox的Text屬性。javascript

二,springMVC支持的數據綁定方式

SpringMVC的各類參數包括對象java對象,集合,Map以及基本數據類型的綁定方式html

1.基本類型,包裝類型的綁定

    1.1基本數據類型的綁定

        基本類型的數據綁定須要注意的是:後臺請求方法中聲明的參數前臺是必需傳的,其次是類型必須相同java

controller類:web

@Controller
public class DataBind {
//    http://localhost:8080/dataBind/test.do?age=11
      @RequestMapping("/test")
    public String  test(int age){

        System.out.println(age);

    return "success";
        }
}

 form表單ajax

<form action="test.do" method="post">  
   <input name="age" value="11" type="text"/>  
   ......  
</form>

注意:
           1.參數名一致:表單中input的name值和Controller的參數變量名保持一致 ,就能完成基本數據類型的數據綁定.   spring

            2.參數類型一致:若是在後臺參數定義的是int類型,那麼前臺傳的值也只能是int類型不然springMVC會進行攔截報一個400參數錯誤(數據轉換的異常)json

            3.參數不能爲空:從jsp提交過來的數據爲null或者""的話,會出現500異常。也就是說,必須保證表單傳遞過來的數據不能爲null或"".數組

對於以上問題能夠採用@RequestParam註解來解決spring-mvc

@Controller
public class DataBind {
    //
    @RequestMapping("/test")
    public String  test(@RequestParam(value ="age" ,required = false) int userAge){

        System.out.println(userAge);

    return "success";
        }
}

  @RequestParam(value ="name" ,required = false) value值是參數別名與表單一致,required 默認值true爲必傳.mvc

    1.2包裝類型的綁定

        controller類:

@RequestMapping("/test3")
    public  String test3(Integer age){
        
        System.out.println(age);

    }

        form表單:

<form action="test.do" method="post">  
   <input name="age" value="10" type="text"/>  
   ......  
</form>

和基本數據類型基本同樣,不一樣之處在於,JSP表單傳遞過來的數據能夠爲null或"",以上面代碼爲例,若是jsp中num爲""或者表單中無age這個input,那麼,Controller方法參數中的num值則爲null。

 

2.簡單對象,複雜對象的綁定

    2.1簡單對象&多層對象

   1. 簡單POJO類對象:

public class User {  
  
    private String firstName;  
  
    private String lastName;  
  
   //省略get&set ...  
  
}

    controller類

@RequestMapping("/test4")
    public  String test4(User user){

        System.out.println(user);
        return "success";
    }

    form表單

<form action="test.do" method="post">  
   <input name="firstName" value="張" type="text"/>  
   <input name="lastName" value="三" type="text"/>  
   ......  
</form>

簡單對象:請求方式的參數Key即爲對象屬性名,不用加「對象名.」的前綴

2.多層對象  

POJO對象

public class ContactInfo {  
  
    private String tel;  
  
    private String address;  
  
    //。。。省略get&set  
  
}  
  
public class User {  
  
    private String firstName;  
  
    private String lastName;  
  
    private ContactInfo contactInfo;  
  
   // 。。。 省略get&set

controller類和簡單對象沒有什麼區別,參數類型都是User(pojo類型對象)

form表單

<form action="<%=basePath%>test5.do" method="get">
    姓:<input name="firstName"/>
    名:<input name="lastName"/>
    地址:<input name="contactInfo.address"/>
...
</form>

   多層級對象:第二級以上對象必須加「對象名.」的前綴。

    2.2.同屬性的多對象

同屬性的多對象 在control中聲明@initBinder的WebDataBinder的前綴

當兩個對象含有相同屬性時,爲了精確的匹配綁定一種方法是經過命名的規範去規避,另外一中解決辦法使用@InitBinder對請求參數加前綴

java代碼

@Controller  
public class dataBind{  
    @InitBinder("teacher")  
    public void initBinder1(WebDataBinder binder){  
        binder.setFieldDefaultPrefix("teacher.");  
    }  
    @InitBinder("student")  
    public void initBinder2(WebDataBinder binder){  
        binder.setFieldDefaultPrefix("student.");  
    }      
      
    //URL映射  
    @RequestMapping(value="/save", method = RequestMethod.GET)  
    public ModelAndView save(Teacher teacher,Student student) {  
        System.out.println(teacher.getName()+" "+student.getName());  
        return null;  
    }

注意:當save的參數名與對象名不一致(即參數名不爲類名的小寫)時,須要在參數前加@ModelAttribute()註解,同時保證@InitBinder的value值與@ModelAttribute的value值一致,若是不指定value值,那麼全部的都將使用。

這種方式的缺點:

一、不支持Path variable的綁定,如/test1/{user1.id}這種狀況的綁定;

二、不支持如集合/數組的綁定;

@InitBinder("user1")
    public  void initUser(WebDataBinder binder){
        binder.setFieldDefaultPrefix("user.");
    }
    @InitBinder("student1")
    public  void initStudent(WebDataBinder binder){
        binder.setFieldDefaultPrefix("student.");
    }
  @RequestMapping("/test6")
    public  String test6(@ModelAttribute("student1")Student a,@ModelAttribute("user1")User b){
        System.out.println(a);
        System.out.println(b);

    return "success";
    }

3.List,Set,Map類型的數據綁定

    3.1 List類型的數據綁定

springMVC 不支持list類型的直接轉換,需包裝成object。

public class User {  
  
    private String firstName;  
  
    private String lastName;  
  
    // 。。。 省略get&set 
  
}  
  
       public class UserListForm {  
  
    private List<User> users;  
  
    // 。。。 省略get&set
  
}
@RequestMapping("test")  
public void test(UserListForm userForm) {  
    for (User user : userForm.getUsers()) {  
        System.out.println(user.getFirstName() + " - " + user.getLastName());  
    }  
}

form表單

<form action="test.do" method="post">  
   <table>  
      <thead>  
         <tr>  
            <th>First Name</th>  
            <th>Last Name</th>  
         </tr>  
      </thead>  
      <tfoot>  
         <tr>  
            <td colspan="2"><input type="submit" value="Save" /></td>  
         </tr>  
      </tfoot>  
      <tbody>  
         <tr>  
            <td><input name="users[0].firstName" value="aaa" /></td>  
            <td><input name="users[0].lastName" value="bbb" /></td>  
         </tr>  
         <tr>  
            <td><input name="users[1].firstName" value="ccc" /></td>  
            <td><input name="users[1].lastName" value="ddd" /></td>  
         </tr>  
         <tr>  
            <td><input name="users[2].firstName" value="eee" /></td>  
            <td><input name="users[2].lastName" value="fff" /></td>  
         </tr>  
      </tbody>  
   </table>  
</form>

這裏的UserListForm對象裏面的屬性被定義成List,而不是普通自定義對象。因此,在JSP中須要指定List的下標。值得一提的是,Spring會建立一個以最大下標值爲size的List對象,因此,若是JSP表單中有動態添加行、刪除行的狀況,就須要特別注意,譬如一個表格,用戶在使用過程當中通過屢次刪除行、增長行的操做以後,下標值就會與實際大小不一致,這時候,List中的對象,只有在jsp表單中對應有下標的那些纔會有值,不然會爲null.

list集合綁定的時候 下標最好是連續的 不然可能形成後臺資源浪費

    3.2 Set類型的數據綁定

Set和List相似,也須要綁定在對象上,而不能直接寫在Controller方法的參數中。可是,綁定Set數據時,必須先在Set對象中add相應的數量的,即Set綁定時需初始化

public class User {  
  
    private String firstName;  
  
    private String lastName;  
//...省略set&get  
  
}  
  
public class UserSetForm {  
  
    private Set<User> users = new HashSet<User>();  
      
    public UserSetForm(){  
        users.add(new User());  
        users.add(new User());  
        users.add(new User());  
    }  
  
   //...省略set&get  
  
}

要使用Set的排重功能必須在對象中覆寫hashcode和equals方法,至於爲何要重寫hashcode和equals方法可參見:http://www.javashuo.com/article/p-drelupuc-hb.html

<form action="test.do" method="post">  
   <table>  
      <thead>  
         <tr>  
            <th>First Name</th>  
            <th>Last Name</th>  
         </tr>  
      </thead>  
      <tfoot>  
         <tr>  
            <td colspan="2"><input type="submit" value="Save" /></td>  
         </tr>  
      </tfoot>  
      <tbody>  
         <tr>  
            <td><input name="users[0].firstName" value="aaa" /></td>  
            <td><input name="users[0].lastName" value="bbb" /></td>  
         </tr>  
         <tr>  
            <td><input name="users[1].firstName" value="ccc" /></td>  
            <td><input name="users[1].lastName" value="ddd" /></td>  
         </tr>  
         <tr>  
            <td><input name="users[2].firstName" value="eee" /></td>  
            <td><input name="users[2].lastName" value="fff" /></td>  
         </tr>  
      </tbody>  
   </table>  
</form>

    基本和List綁定相似。
要特別提醒的是,若是最大下標值大於Set的size,則會拋出org.springframework.beans.InvalidPropertyException異常

spingMVC在對集合進行綁定時,優先選擇List

3.3 Map類型的數據綁定

Map類型的數據綁定也能用在對象的去重,由於Map的key值是惟一的.

public class User {  
  
    private String firstName;  
  
    private String lastName;  
  
    。。。  
  
}  
  
public class UserMapForm {  
  
    private Map<String, User> users;  
  
    。。。  
  
}  

@RequestMapping("test")  
public void test(UserMapForm userForm) {  
    for (Map.Entry<String, User> entry : userForm.getUsers().entrySet()) {  
        System.out.println(entry.getKey() + ": " + entry.getValue().getFirstName() + " - " +  
                                 entry.getValue().getLastName());  
    }  
}
<form action="test.do" method="post">  
   <table>  
      <thead>  
         <tr>  
            <th>First Name</th>  
            <th>Last Name</th>  
         </tr>  
      </thead>  
      <tfoot>  
         <tr>  
            <td colspan="2"><input type="submit" value="Save" /></td>  
         </tr>  
      </tfoot>  
      <tbody>  
         <tr>  
            <td><input name="users['x'].firstName" value="aaa" /></td>  
            <td><input name="users['x'].lastName" value="bbb" /></td>  
         </tr>  
         <tr>  
            <td><input name="users['y'].firstName" value="ccc" /></td>  
            <td><input name="users['y'].lastName" value="ddd" /></td>  
         </tr>  
         <tr>  
            <td><input name="users['z'].firstName" value="eee" /></td>  
            <td><input name="users['z'].lastName" value="fff" /></td>  
         </tr>  
      </tbody>  
   </table>  
</form>

x即爲Map的key,firstName即爲User對象的屬相

4.Json,Xml類型的數據綁定

    4.1Json類型的數據綁定

@RequestBody把傳過來的Json數據反序列化綁定到控制器參數上

對於JOSN類型的參數綁定通常應用的場景是在使用AJax請求.而在SpringMVC環境中,@RequestBody接收的是一個Json對象的字符串,而不是一個Json對象.能夠用 JSON.stringify(data)的方式就能將對象變成字符串。同時ajax請求的時候也要指定dataType: "json",contentType:"application/json" 這樣就能夠輕易的將一個對象或者List傳到Java端,使用@RequestBody便可綁定對象或者List.

JavaScript 代碼:

<script type="text/javascript">  
    $(document).ready(function(){  
        var saveDataAry=[];  
        var data1={"userName":"test","address":"gz"};  
        var data2={"userName":"ququ","address":"gr"};  
        saveDataAry.push(data1);  
        saveDataAry.push(data2);         
        $.ajax({ 
            type:"POST", 
            url:"user/saveUser", 
            dataType:"json",      
            contentType:"application/json",               
            data:JSON.stringify(saveData), 
            success:function(data){ 
               //...tosomething                        
            } 
         }); 
    });  
</script>

    controller方法:

@RequestMapping(value = "test", method = {RequestMethod.POST }}) 
    @ResponseBody  
    public void saveUser(@RequestBody List<User> users) { 
         userService.batchSave(users); 
    }

4.2Xml類型的數據綁定

1.SpingMVC對象Xml類型的數據綁定須要spring-oxm jar包支持.一樣也是@RequestBody把傳過來的Xml數據反序列化綁定到控制器參數上

2.xml 數據綁定:必須在實體類裏面加註解@XmlRootElement(根節點),在屬性上添加XmlElement (子節點)ex:@XmlElement(name="age"):此時就會將xml 裏面對應的age數據添加到實體類中的age屬性中去。

model對象的處理:

@XmlRootElement(name="admin")//根節點別名
public class Admin {
    private  String name;
    private  String age;
    @XmlElement(name="name")//在get方法在夾@XmlElement 子節點別名
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    @XmlElement(name ="age")
    public String getAge() {

        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

xml數據

<? version="1.0" encoding="utf-8" ?>
<admin>
 <name>張三</name>
 <age>22</age>
</admin>

5.自定義類型轉化器(PropertyEditor Formatter Converter)

5.1PropertyEditor的應用

先來看Sun提供的PropertyEditor屬性編輯器

PropertyEditor是java提供的屬性編輯器接口,PropertyEditorSupport是直接實現類,一般都是繼承該實現類並重寫setAsText(String) 方法,實現從String類型到自定類型的裝換.

spring中也有繼承了PropertyEditorSupport並重寫了setAsText的自定義的屬相編輯器,都在spring-beans包下的org.springframework.beans.propertyeditors包裏:

在SpringMVC中主要是WebDataBinder實現對數據的轉換

PropertyEditorRegistyr封裝方法來給JavaBean註冊對應的屬性編輯器。

實現方式代碼;

/**
     * 在controller層中加入一段數據綁定代碼
     * @param webDataBinder
     */
    @InitBinder
    public void initBinder(WebDataBinder webDataBinder) throws Exception{
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
        simpleDateFormat.setLenient(false);
        webDataBinder.registerCustomEditor(Date.class , new CustomDateEditor(simpleDateFormat , true));
//CustomDateEditor是spring提供的繼承PropertyEditorSupport的類,也能夠換成自定義的MyCustomPropertyEditor類
    }
   //備註:自定義類型轉換器必須實現PropertyEditor接口或者繼承PropertyEditorSupport類
MyCustomPropertyEditor extends propertyEditorSupport{
     public void setAsText(String text){
         SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy -MM-dd hh:mm");
        Date date = simpleDateFormat.parse(text);
        this.setValue(date);
     }
     public String getAsTest(){
      Date date = (Date)this.getValue(); 
      return this.dateFormat.format(date);
     }
}

這種使用Property屬性編輯器的方法須要在controller層加一段數據綁定的代碼,不夠靈活,不具備全局性

5.2Formatter的應用

要使用全局的數據轉換器,在Spring 3.0後可使用Converter和Formatter,都是用來作數據轉換的

Formatter繼承了Printer和Parser接口

public interface Formatter<T> extends Printer<T>, Parser<T> {  
  
}

Printer用來打印轉換後的對象 

public interface Printer<T> {  
  
    /** 
     * Print the object of type T for display. 
     * @param object the instance to print 
     * @param locale the current user locale 
     * @return the printed text string 
     */  
    String print(T object, Locale locale);  
  
}

,Parser用來從String類型轉換爲指定類型值

public interface Parser<T> {  
  
    /** 
     * Parse a text String to produce a T. 
     * @param text the text string 
     * @param locale the current user locale 
     * @return an instance of T 
     * @throws ParseException when a parse exception occurs in a java.text parsing library 
     * @throws IllegalArgumentException when a parse exception occurs 
     */  
    T parse(String text, Locale locale) throws ParseException;  
  
}

從方法上,能夠看分出Formatter是從String類型轉換爲任意目標類型<T>,有點相似PropertyEditor

代碼實現:

public class MyDateFormatter implements Formatter<Date> {
    @Override
    public String print(Date object, Locale locale) {
        return null;
    }
    //只需重寫parse方法 實現類型轉換爲目標類型<T>
    @Override
    public Date parse(String text, Locale locale) throws ParseException {

        return new SimpleDateFormat("yyyy-MM-dd").parse(text);
    }
}

在spring-mvc.xml的配置文件中須要註冊自定義的轉換器實現全局轉換

<mvc:annotation-driven conversion-service="MyDateFormatter"/>
    <bean id="MyDateFormatter"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="formatters">
        <set>
            <bean class="com.chuyu.ssm.controller.MyDateFormatter"/>
        </set>
    </property>
    </bean>

雖然這個方法能夠實現全局的數據轉換,可是支持的類型也只能是從String類型轉換爲任意目標類型<T>.

5.3Converter的應用

public interface Converter<S, T> {  
  
    /** 
     * Convert the source object of type {@code S} to target type {@code T}. 
     * @param source the source object to convert, which must be an instance of {@code S} (never {@code null}) 
     * @return the converted object, which must be an instance of {@code T} (potentially {@code null}) 
     * @throws IllegalArgumentException if the source cannot be converted to the desired target type 
     */  
    T convert(S source);  
  
}

從源碼能夠看出去,Converter接口能夠從任意源類型,轉換爲任意目標類型

代碼實現:

package com.chuyu.ssm.controller;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.core.convert.converter.Converter;
/**
 * 全局類型轉換器
 * @author acer
 *
 */
public class DateConvert  implements Converter<String, Date>{
    @Override
    public Date convert(String text) {
        Date date = null;
        try {
            if(text.contains("-")){
                SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
                date = sf.parse(text);
            }else {
                SimpleDateFormat sf = new SimpleDateFormat("dd/MM/yyyy");
                date = sf.parse(text);
            }
            
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

}

類型轉換器須要在配置文件中配置:

<!--開啓註解  -->    
<mvc:annotation-driven conversion-service="MyDataConverterService" />

<!--類型轉換器工廠  -->

<bean id="MyDataConverterService"
      class="org.springframework.context.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="com.cy.springannotation.controller.DateConvert" />
        </list>
    </property>
</bean>
相關文章
相關標籤/搜索