在字符串到实体转换一文中介绍了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。