SpringMVC類型轉換器、屬性編輯器


對於MVC框架,參數綁定一直覺得是很神奇很方便的一個東西,在參數綁定的過程中利用了屬性編輯器、類型轉換器

參數綁定流程

參數綁定:把請求中的數據,轉化成指定類型的對象,交給處理請求的方法

編輯器轉換器

  • 請求進入到DisptacherServlet,卸下請求中的數據
  • DisptacherServlet將請求中的數據發送給Controller
  • 獲取Controller需要接收的參數類型,將參數類型和請求數據發送給DataBinder
  • DataBinder將參數類型和請求數據再發給TypeConverter,由TypeConverter裝配成一個bean
  • TypeConverter根據bean中的成員類型,在PropertyEditorRegistry中查找已注冊的PropertyEditor
  • PropertyEditor將數據setter進bean中的成員
  • TypeConverter將裝配好的bean返回給DataBinder
  • DataBinder將裝配bean交給處理請求的方法

在參數綁定的過程TypeConverter和PropertyEditor是最核心的數據轉化成對象(非序列化)的過程TypeConverter負責將數據轉化成一個beanPropertyEditor負責將數據轉化成一個成員字段

屬性編輯器

PropertiesEditor負責轉化簡單對象,因為http請求都是以字符串的形式,所以一般都是根據String來轉換springmvc提供了很多默認的屬性編輯器,在org.springframework.beans.propertyeditors包中,比如

微信截圖_20160716120316

  • CustomBooleanEditor.class,String 轉換 Boolean
  • CustomCollectionEditor.class,String 轉換 Collection
  • CustomDateEditor.class,String 轉換 Date
  • CustomMapEditor.class,String 轉換 Map
  • CustomNumberEditor.class,String 轉換  int、floot、double..

所有的屬性編輯器都是繼承PropertiesEditorSupport,默認的屬性編輯器,Spring在啟動的時候會自動加載除此之外,如果要裝配的屬性沒有合適的編輯器,還可以自定義屬性編輯器注冊了自定義的屬性編輯器之后,在CustomEditorConfigurer中注冊,應用全局都可以使用這個屬性編輯器,因為屬性編輯器的工廠是全局作用域的

PropertiesEditor源碼分析

PropertiesEditor.java

public class PropertiesEditor extends PropertyEditorSupport {
 //將String轉成指定類型的對象
 @Override
 public void setAsText(String text) throws IllegalArgumentException {
  Properties props = new Properties();//Properties以key-value存值
   if (text != null) {
   try {
   //將String中表示的key=value或key:value信息,轉化成Properties
   //key表示bean中字段名稱
   //如果要轉化成Date,則value是Date,String可以是"date=2012-12-12"的形式(date是字段名)
   props.load(new ByteArrayInputStream(text.getBytes("ISO-8859-1")));
  }
  catch (IOException ex) {
   throw new IllegalArgumentException(
   "Failed to parse [" + text + "] into Properties", ex);
   }
 }
  setValue(props);
 }
 //將old object轉化成新object
 @Override
 public void setValue(Object value) {
   if (!(value instanceof Properties) && value instanceof Map) {
   Properties props = new Properties();
   props.putAll((Map<?, ?>) value);
   super.setValue(props);
 }
 else {
   //父類PropertyEditorSupport持有value對象,就是要轉化后的對象
   super.setValue(value);
  }
 }
}

需要注意的是,setAsText通過一定格式的字符串來達到屬性編輯的效果,"成員名稱=value",或者是"成員名稱:value",這樣就會把value set到bean的指定成員中了編輯器中最重要的兩個方法就是,setAsTest(String)和setValue(value),在這兩個方法中完成從String——object,object——object

CustomDateEditor源碼分析

CustomDateEditor是Spring的一個默認屬性編輯器,負責將String轉化成指定格式的Date對象同樣他也是繼承了PropertiesEditorSupport,重寫了setAsTest方法

public class CustomDateEditor extends PropertyEditorSupport {
 //指定的date格式,如"yyyy-MM-dd"
 private final DateFormat dateFormat;
 //是否允許字符串為空
 private final boolean allowEmpty;
 //嚴格的日期長度
 private final int exactDateLength;

 public CustomDateEditor(DateFormat dateFormat, boolean allowEmpty) {
 //構造器方法
 }
 public CustomDateEditor(DateFormat dateFormat, boolean allowEmpty, int exactDateLength) {
 //構造器方法
 }
 //String轉化成Dtae
 @Override
 public void setAsText(String text) throws IllegalArgumentException {
   //判斷字符串是否為空
   if (this.allowEmpty && !StringUtils.hasText(text)) {
    setValue(null);
   }
   //判斷字符串長度是否等於exactDateLength
   else if (text != null && this.exactDateLength >= 0 && text.length() != this.exactDateLength) {
     throw new IllegalArgumentException(
     "Could not parse date: it is not exactly" + this.exactDateLength + "characters long");
    }
   else {
   try {
     //將text格式化成Date對象
     setValue(this.dateFormat.parse(text));
   }
   catch (ParseException ex) {
     throw new IllegalArgumentException("Could not parse date: " + ex.getMessage(), ex);
   }
   }
   }
 //從Date輸出String
 @Override
 public String getAsText() {
   Date value = (Date) getValue();
   //返回格式化的String
   return (value != null ? this.dateFormat.format(value) : "");
 }
}

從CustomDateEditor的源碼可以看出,最重要的是重寫setAsText方法,先校驗下字符串格式符不符合要求,不符合要求就拋出異常,再根據字符串轉成指定DateFormat的Date對象

類型轉換器

剛剛講的屬性編輯器是用來填充bean中的屬性的,類型轉換器是負責從數據轉換成一個bean所以在轉換的過程中,需要屬性編輯器幫忙填充屬性,那么應該持有一堆屬性編輯器(bean有各種各樣的屬性),那么持有一個PropertyEditorRegistry(一個屬性編輯器工廠)就可以了類型轉化器的實現不像屬性編輯器那么多,主要就是三個

  • TypeConverter,類型轉換的接口
  • TypeConverterSupport,類型轉換的實現,持有一個TypeConverterDelegate,具體轉換工作交給TypeConverterDelegate完成
  • TypeConverterDelegate,類型轉換的委托類,所有類型轉換的工作都由他完成

typeConverter

要實現的方法就只有convertIfNecessary,從源對象轉換為目標對象

TypeConverterDelegate源碼分析

因為轉換工作是由TypeConverterDelegate負責的,源碼太長,就看看轉換那一部分的代碼

/*
@Param propertyName bean的名稱
@Param requiredType 需要的類型
@Param typeDescriptor 類型描述器
*/
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
 //從注冊的屬性編輯器中獲取能編輯requiredType的屬性編輯器
 PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
 //...
 //使用屬性編輯器去把oldValue轉化成requiredType
 convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
 //...
 return convertedValue;
 }
/*
@Param propertyName bean的名稱
@Param requiredType 需要的類型
@Param editor 屬性編輯器
*/
 private Object doConvertValue(Object oldValue, Object newValue, Class<?> requiredType, PropertyEditor editor) {
   Object convertedValue = newValue;
   if (editor != null && !(convertedValue instanceof String)) {
   try {
     //轉換數據
     editor.setValue(convertedValue);
     //得到轉換后的數據
     Object newConvertedValue = editor.getValue();
     if (newConvertedValue != convertedValue) {
     convertedValue = newConvertedValue;
     editor = null;
    }
 }
   catch (Exception ex) {
     if (logger.isDebugEnabled()) {
     logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
    }
   }
 }
 Object returnValue = convertedValue;
 //...
 return returnValue;
 }
}

 查看原文:http://zswlib.com/2016/07/16/springmvc%E7%B1%BB%E5%9E%8B%E8%BD%AC%E6%8D%A2%E5%99%A8%E3%80%81%E5%B1%9E%E6%80%A7%E7%BC%96%E8%BE%91%E5%99%A8/


免責聲明!

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



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