在字符串到實體轉換一文中介紹了Spring核心框架中使用PropertyEditor將任何字符串轉換爲數字、實體的方法。除了字符串到實體,Spring還提供了更加通用的功能在對象和對象之間進行數據轉換。html
Spring的類型轉換的基礎是Converter<S, T>(如下簡稱轉換器)接口:java
package org.springframework.core.convert.converter; public interface Converter<S, T> { T convert(S source); }
光是看他的結構就很清晰的明白這個接口是要作什麼。S表示Source(來源)、T表示Target(目標),因此這個接口的2個範型參數就是數據從S轉換爲T,Converter::convert方法正是輸入一個「S」類型的實例,返回一個「T」類型的實例。web
能夠經過這個接口實現規範化、可複用的類型轉換功能。下面經過轉換器實現字符串到PC實體類相互轉換的過程。spring
Pc實體:數據結構
public class PC extends Device { String cpu; String graphic; String ram; //Getter & Setter ... }
在基類Device中經過反射實現字符串到實體類的轉換:mvc
public abstract class Device { public void pares(String text){ //字符串轉換爲實體 Field[] fields = this.getClass().getDeclaredFields(); for (Field field : fields) { int begIndex = text.indexOf(field.getName()); int endIndex = text.indexOf(";", begIndex); String sub = text.substring(begIndex, endIndex), value = sub.split("=")[1]; field.setAccessible(true); field.set(this, value); } }; public String value(){ //實體轉換爲字符串 Field[] fields = this.getClass().getDeclaredFields(); StringBuilder sb = new StringBuilder(); for (Field field : fields) { sb.append(field.getName()); sb.append("="); sb.append(field.get(this).toString()); sb.append(";"); } return sb.toString(); } }
而後聲明兩個轉換器的實現類:app
public class String2PcConverter implements Converter<String, PC> { //字符串轉換爲PC對象 @Override public PC convert(String source) { PC pc = new PC(); pc.pares(source); return pc; } }
public class PC2StringConverter implements Converter<PC, String> { //PC對象轉換爲字符串 @Override public String convert(PC source) { return source.value(); } }
最後使用這兩個轉換器:框架
public class ConversionApp { void singletonConversion() { final String text = "cpu=amd;ram=kingston;graphic=Navidia;"; Converter<String, PC> string2Pc = new String2PcConverter(); PC pc = string2Pc.convert(text); Converter<PC, String> pc2String = new PC2StringConverter(); String string = pc2String.convert(pc); } }
以上就是Spring最基本的類型轉換功能——圍繞着轉換器(Converter<S, T>)接口實現數據類型轉換。看到這裏可能有些碼友就要問了:這到底有什麼用?直接用使用Device::pares和Device::value方法不就完事了?爲何還要引入轉換器兜一圈??!ide
若是系統僅僅只有1個或幾個類型轉換確實不必引入轉換器。可是業務老是繁雜多樣的,模塊與模塊以前也會存在數據結構的差別,所以咱們須要適配器(Adapter)、外觀(Facade)等模式來應對變化無窮的外部輸入而無需改動業務邏輯。實際上從更高的層次看,Converter接口就是Spring爲類型轉換提供的一個適配器。後面會看到Spring已經爲程序的順利運行提供了大量的轉換器,即便在閱讀本文內容以前不知道這些轉換器的存在,但Spring框架時時刻刻都在使用他們。ui
轉換器只能對單一類型進行轉換,若是有大量相同類別的數據須要轉換可使用ConverterFactory(一下簡稱轉換工廠):
public interface ConverterFactory<S, R> { <T extends R> Converter<S, T> getConverter(Class<T> targetType); }
ConverterFactory::getConverter是返回一個轉換器,這裏範型標記「T」是「R」的子類。看下面轉換工廠的例子,他能夠將字符串轉換成Device的子類:
public class String2DeviceConverterFactory implements ConverterFactory<String, Device> { public <T extends Device> Converter<String, T> getConverter(Class<T> targetType) { return new String2DeviceConverter(targetType); } // Device的通用轉換器 static class String2DeviceConverter<T extends Device> implements Converter<String, Device> { private Class<? extends Device> klass; public String2DeviceConverter(Class<? extends Device> klass) { this.klass = klass; } public T convert(String source) { Device device = null; device = klass.newInstance(); device.pares(source); return (T) device; } } }
而後可使用這個轉換工廠按照目標類型進行轉換:
public class ConversionApp { void factoryConversion() { String2DeviceConverterFactory factory = new String2DeviceConverterFactory(); Converter<String, PC> pcConverter = factory.getConverter(PC.class); //將字符串轉換爲PC PC pc = pcConverter.convert("cpu=amd;ram=kingston;graphic=Navidia;"); Converter<String, Phone> phoneConverter = factory.getConverter(Phone.class); //將字符串轉換爲Phone Phone phone = phoneConverter.convert("name=HUAWEIP20;cpu=Kirin970;ram=64G;"); } }
Phone是另一個繼承了Device的實體類:
public class Phone extends Device { String name; String cpu; String ram; // Getter & Setter }
Spring已經爲數據轉換預設了大量的Converter,這些Converter能夠經過ConversionService直接使用。ConversionService中包含了幾乎全部Java常規類型的數據格式轉換,看下面的案例。
public class ConversionApp {ConversionApp registConversionService() { ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(ConversionConfig.class); // 獲取ConversionService ConversionService service = ctx.getBean(ConversionService.class); // 字符串轉換爲整型 int i = service.convert("123456", Integer.class); // 字符串轉換爲浮點 float f = service.convert("1234.56", Float.class); // 源生列表轉換爲List List<?> list = service.convert(new int[] { 1, 2, 3, 4, 5, 6 }, List.class); // 源生列表轉換爲Set Set<?> set = service.convert(new int[] { 1, 2, 3, 4, 5, 6 }, Set.class); // 枚舉轉換 Gender gender = service.convert("Male", Gender.class); // 使用自定義轉換器 PC pc = service.convert("cpu=amd;ram=kingston;graphic=Navidia;", PC.class); // UUID轉換 UUID uuid = service.convert("f51b4b95-0925-4ad0-8c62-4daf3ea7918f", UUID.class); // 字符串轉換爲Optional<PC> Optional<PC> options = service.convert("cpu=amd;ram=kingston;graphic=Navidia;", Optional.class); // 使用TypeDescriptor描述進行轉換 String source = "123456789"; int result = (int) service.convert(source, TypeDescriptor.valueOf(source.getClass()), TypeDescriptor.valueOf(Integer.class)); _G.print(result); } enum Gender { Male, Female, Other } }
除了上面的轉換,ConversionService還提供了其餘轉換器,詳情請看org.springframework.core.convert.support.DefaultConversionService的JavaDoc文檔。
須要經過ConversionServiceFactoryBean來啓用ConversionService,下面的代碼是在@Configurable中向IoC容器添加ConversionServiceFactoryBean:
@Configurable public class ConversionConfig { @Bean public ConversionServiceFactoryBean ConversionServiceFactoryBean() { ConversionServiceFactoryBean factoryBean = new ConversionServiceFactoryBean(); Set<Converter> converters = new HashSet<>(); // 添加自定義轉換器 converters.add(new String2PcConverter()); converters.add(new PC2StringConverter()); factoryBean.setConverters(converters); return factoryBean; } }
也能夠經過XML文件配置來引入ConversionService:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="chkui.springcore.example.javabase.conversion.support.PC2StringConverter"/> <bean class="chkui.springcore.example.javabase.conversion.support.String2PcConverter"/> </set> </property> </bean>
ConversionService在Spring MVC中的做用很大,能夠全局註冊統一的類型轉換器,詳情請見 Conversion and Formatting。