在 Spring MVC 4 的架構中,類型轉換系統經過全面重設計,提供了比舊版 PropertyEditor 更強大、更靈活的轉換機制。本文將深入解析 Spring 核心轉換接口的設計哲學,並透過完整實例示範如何實作自定義轉換器以滿足實際開發中的個性化需求。
Spring 的類型轉換體系建立在三個核心接口之上,開發者可根據場景複雜度選擇實作:
最簡單的轉換接口,定義單向類型轉換邏輯:
public interface Converter<S, T> {
T convert(S source);
}
適用於簡單的一對一類型轉換,如 String → User 物件。
支援根據源類型與目標類型的宿主類上下文資訊進行轉換:
public interface GenericConverter {
Set<ConvertiblePair> getConvertibleTypes();
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
其擴展介面 ConditionalGenericConverter 更結合 ConditionalConverter,允許根據上下文條件動態決定是否執行轉換,極大提升靈活性。
用於處理「單一來源類型轉換為目標類型及其子類型」的場景:
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
典型實現如 StringToNumberConverterFactory,可將字串轉換為 Number 及其所有子類。
ConversionService 是類型轉換體系的核心協調者,提供統一的轉換入口。在 Spring 容器中,透過 ConversionServiceFactoryBean 完成轉換器的註冊與初始化:
<mvc:annotation-driven conversion-service="conversionService" />
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.example.MyConverter" />
</set>
</property>
</bean>
將請求參數 user=11,myUserName,myPassword 自動轉換為 User 物件。
public class User {
private Integer id;
private String userName;
private String password;
// 建構子、Getter/Setter、toString() 略
}
@Component
public class MyConverter implements Converter<String, User> {
@Override
public User convert(String source) {
String[] values = source.split(",");
Integer id = Integer.valueOf(values[0]);
return new User(id, values[1], values[2]);
}
}
@RequestMapping("/convert")
public String convert(User user) {
System.out.println(user); // 輸出: User [id=11, userName=myUserName, password=myPassword]
return "view";
}
關鍵點:請求參數名 user 必須與控制器方法參數名一致,才能觸發資料綁定與類型轉換。
新增 SuperUser extends User,需根據目標類型動態選擇轉換策略。
public class SuperUser extends User {
private String name;
public SuperUser(String name, User user) {
super(user.getId(), user.getUserName(), user.getPassword());
this.name = name;
}
}
@Component
public class MySuperConverter implements Converter<String, SuperUser> {
@Override
public SuperUser convert(String source) {
String[] v = source.split(",");
return new SuperUser(v[3], new User(Integer.valueOf(v[0]), v[1], v[2]));
}
}
@Component
public class MyConverterFactory implements ConverterFactory<String, User> {
@Override
public <T extends User> Converter<String, T> getConverter(Class<T> targetType) {
if (targetType == User.class) {
return (Converter<String, T>) new MyConverter();
}
return (Converter<String, T>) new MySuperConverter();
}
}
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.example.MyConverterFactory" />
</set>
</property>
</bean>
@RequestMapping("/convert")
public String convert(User user) { ... }
@RequestMapping("/convertSuper")
public String convertSuper(SuperUser user) { ... }
訪問 /convert?user=10,myUserName,myPassword 輸出 User;訪問 /convertSuper?superUser=11,myUserName,myPassword,myName 輸出 SuperUser,完美實現多態轉換。
Converter 處理簡單場景GenericConverter 或 ConditionalGenericConverterConverterFactory 是最佳選擇FormattingConversionServiceFactoryBean,保留對 @NumberFormat、@DateTimeFormat 等註解的支援掌握 Spring 類型轉換體系,能讓控制器層保持乾淨,將複雜的資料轉換邏輯封裝於專屬轉換器中,符合單一職責原則,提升程式碼可維護性。