springMVC4(10)強大類型轉換器實例解析


《springMVC4(9)屬性編輯器剖析入參類型轉換原理 》一文中,我們通過分析Sping內置的屬性編輯器來理解springMVC是如何完成請求參數到入參的類型的轉換的。而在新版本中,SpringMVC使用了新的架構來完成類型轉換的工作,而且它的工作更加強大,支持格式化參數輸入輸出,它的另一個實例可見我的另一篇文章《springMVC4(4)json與對象互轉實例解析請求響應數據轉換器》。在文中,我們使用了Spring內置的格式轉換器完成了服務端輸入輸出過程中json字符串與java對象的相互轉換。此外,還提到了其他多種的格式轉換器,如xml、ByteArray等。下面,我們以自定義格式轉換器的實現思路,來理解新架構的類型轉換器的使用方法,同時在實際開發中,我們可能會有自己的格式轉換需求,這個時候我們也可以通過自定義格式轉換器來完成這些個性化需求。

自定義格式轉換器

完成自定義轉換器需要實現以下三個中的任意一個接口:Convertor<S,T>、GenericConvertor或ConvertorFacoty。下面我們對這些接口進行逐一分析:

1. Convertor<S,T>

這是最為簡單的一個接口,定義了從源類到目標類的轉換方法。該接口的定義如下

public interface ConverterFactory<S, R> {
//將S類型的對象轉換為T類型,R為目標類型T的基類
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

2. GenericConvertor

GenericConvertor會根據源類對象及目標類對象所在宿主類的上下文信息進行類型轉換工作,該接口的定義如下:

public interface GenericConverter {

//ConvertiblePair包含了源類型和目標類型,它的定義在下面
Set<ConvertiblePair> getConvertibleTypes();

//TypeDescriptor包含了需轉換類型對象所在宿主類的信息,我們根據此信息,完成源到目標類型的轉換
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);


/**
* 內部類定義
*/

public static final class ConvertiblePair {
//源類型
private final Class<?> sourceType;
//目標類類型
private final Class<?> targetType;

/**
* 創建一個源-目標對子
*/

public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(targetType, "Target type must not be null");
this.sourceType = sourceType;
this.targetType = targetType;
}

public Class<?> getSourceType() {
return this.sourceType;
}

public Class<?> getTargetType() {
return this.targetType;
}
//忽略hashCode\equals\toString等重寫方法
}
}

我們常使用其實現類接口:

public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}

它除了實現GenericConverter,還實現了另一個“條件轉換器”:

public interface ConditionalConverter {
/**
* Should the conversion from {@code sourceType} to {@code targetType} currently under
*/

//根據源類型和目標類型所在宿主類型的上下文信息判斷是否要進行類型轉換
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

在實際開發中,我們能實現此接口自定義轉換器,來根據具體類型上下文來靈活配置我們的類型轉換

3. ConvertorFacoty

這是一個將我們源類轉換為一個目標類或其子類的”多轉換器共存“接口工廠。它的定義如下:

public interface ConverterFactory<S, R> {

//獲取將源類轉換為特定R類或其子類的轉換器
<T extends R> Converter<S, T> getConverter(Class<T> targetType);

}

這個接口一個常見的實現類是StringToNumberConvertor,能將String類型數據轉換為Number類型或其子類:Long,Integer,Double等。

注冊自定義轉換器

ConversionService

ConversionService則是Spring類型轉換體系的核心接口,ConversionService接口的定義如下:


package org.springframework.core.convert;

public interface ConversionService {

//判斷sourceType是否可以轉換為targetType
boolean canConvert(Class<?> sourceType, Class<?> targetType);

//TypeDescriptor描述了轉換類的各類上下文信息,在類型轉換實現方法中可以根據這些信息進行靈活控制
//比如這里通過源類和目標類的上下文信息判斷是否可以進行轉換
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);

//將source轉換為targetType
<T> T convert(Object source, Class<T> targetType);

//利用源、目標類的上下文信息,將源類型轉換為目標類型
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);

}

ConversionServiceFactoryBean

實現以上類型完成我們的自定義轉換器定義后,我們還要在Spring容器中通過ConversionServiceFactoryBean注冊創建后才能使用。
ConversionServiceFactoryBean創建了我們的ConversionService很多內置轉換器,利用這些轉換器,我們可以完成大部分常見的類型轉換工作
而如果我們想使用自定義的類型轉換器,可以通過ConversionServiceFactoryBean的convertor屬性來注冊。

實例分析1:測試Convertor

通過以上的分析,我們接下來嘗試自定實現Convert

1. 自定義屬性轉換器

public class MyConvertor implements Converter<String, User>{

@Override
public User convert(String source) {//source為要轉換的字符串
String[] values = source.split(",");//根據我們的需求,用逗號來區分
Integer id = Integer.valueOf(values[0]);
User user = new User(id,values[1],values[2]);
return user;
}
}
/**********下面是我們的UserPOJO類**********/
public class User {
public User() {
super();
}
private Integer id;
private String userName;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public User(Integer id, String userName, String password) {
super();
this.id = id;
this.userName = userName;
this.password = password;
}
//忽略get和set方法
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", password="
+ password + "]";
}

}

2. 注冊自定義屬性轉換器

<!-- 通過:annotation-driven的conversion-service屬性來裝配我們的類型轉換器 -->
<mvc:annotation-driven conversion-service="factoryBean" />
<!-- 通過ConversionServiceFactoryBean注冊我們的自定義轉換器 -->
<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="factoryBean" >
<property name="converters"><!-- 在屬性converters注冊 -->
<list>
<bean class="com.mvc.convertor.MyConvertor" />
</list>
</property>
</bean>

3. 配置控制器

在控制層,我們通過以下方法測試我們的轉換器

@RequestMapping("convert")
public String convert(User user){
System.out.println(user);
return "model1";
}

4. 測試

啟動服務器,在游覽器中訪問[項目根路徑]/convert?user=11,myUserName,myPassword。
控制台會打印信息:User [id=11, userName=myUserName, password=myPassword]。即springMVC幫我們完成了字符串到User類型的轉換。**這里需注意的是,我們的請求參數名”user”是和控制層方法入參變量User user像對應的,才能完成參數綁定進而轉換類型

實例分析2:測試ConvertorFactory

1. 自定義類型轉換器

在實例1的基礎上,我們添加User的一個子類:SuperUser,作為”super”子類,它擁有了自己的專屬名字,我們將字符串”11,myUserName,myPassword,myName“轉換為我們的superUser對象,下面相對應的自定義轉換器和POJO類

public class MySuperConvertor implements Converter<String, SuperUser>{

@Override
public SuperUser convert(String source) {
String[] values = source.split(",");
Integer id = Integer.valueOf(values[0]);
SuperUser superUser = new SuperUser(values[3], new User(id,values[1],values[2]));
return superUser;
}
}
/**********下面是SuperUser POJO類*********/
package com.mvc.model;

public class SuperUser extends User {
private String name;
//忽略get和set方法

public SuperUser(String name,User user) {
super(user.getId(),user.getUserName(),user.getPassword());
this.name = name;
}

public SuperUser() {
super();
}

@Override
public String toString() {
return "SuperUser [name=" + name + ", toString()=" + super.toString()
+ "]";
}
}

除了配置上面的轉換器,還需自定義我們的轉換器工廠,在轉換器工廠中,我們根據目標類型是User還是其子類SuperUser來調用相應的自定義轉換器:

public class MyConvertorFactory implements ConverterFactory<String, User>{

@Override
//T類型必須是User或其子類,Stirng是我們的轉換源類
public <T extends User> Converter<String, T> getConverter(

Class<T> targetType) {
if(targetType == User.class){
return (Converter<String, T>) new MyConvertor();
}else{
return (Converter<String, T>) new MySuperConvertor();
}
}
}

2. 注冊自定義屬性轉換器

<!-- 通過:annotation-driven的conversion-service屬性來裝配我們的類型轉換器 -->
<mvc:annotation-driven conversion-service="factoryBean" />
<!-- 通過ConversionServiceFactoryBean注冊我們的自定義轉換器 -->
<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="factoryBean" >
<property name="converters"><!-- 在屬性converters注冊 -->
<list>
<!--這里只要注冊我們自定義的轉換器工廠即可-->
<bean class="com.mvc.convertor.MyConvertorFactory" />
</list>
</property>
</bean>

3. 配置控制器

在實例1的基礎上,我們添加一個新方法

//這是原來的
@RequestMapping("convert")
public String convert( User user){
System.out.println(user);
return "model1";
}
//下面是新添加的方法
@RequestMapping("convertSuper")
public String convert( SuperUser user){
System.out.println(user);
return "model1";
}

4. 測試

運行服務器,我們在游覽器中輸入:
1. root/convert?user=10,myUserName,myPassword
控制台輸出:User [id=10, userName=myUserName, password=myPassword]
2. root/convertSuper?superUser=11,myUserName,myPassword,myName
控制台輸出:SuperUser [name=myName, toString()=User [id=11, userName=myUserName, password=myPassword]]

我們根據入參類型,並通過ConvertFactory,完成對同一系列(某一類及其子類)的類型轉換

源碼下載

本篇文章測試源碼可到https://github.com/jeanhao/spring的dataConvertor文件夾下下載


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com