在前一篇WebDataBinder中講述了了一個PropertyEditor,它經過setAsText知足了字符串到指定類型的轉換,可是它實現不了從任意類型轉換到目標類型,因此在spring3.x以後引入了Converter,它實現了上述需求的轉換。java
①:類型轉換:內部的ConversionService會根據S源類型/T目標類型自動選擇相應的Converter SPI進行類型轉換,並且是強類型的,能在任意類型數據之間進行轉換;
②:數據驗證:支持JSR-303驗證框架,如將@Valid放在須要驗證的目標類型上便可;
③:格式化顯示:其實就是任意目標類型---->String的轉換,徹底可使用Converter SPI完成。
Spring爲了更好的詮釋格式化/解析功能提供了Formatter SPI,支持根據Locale信息進行格式化/解析,並且該套SPI能夠支持字段/參數級別的細粒度格式化/解析,流程以下:
①:類型解析(轉換):String---->T類型目標對象的解析,和PropertyEditor相似;
②:數據驗證:支持JSR-303驗證框架,如將@Valid放在須要驗證的目標類型上便可;
③:格式化顯示:任意目標類型---->String的轉換,和PropertyEditor相似。web
public interface Converter<S, T> { /** * Convert the source object of type {@code S} to target type {@code T}. * @param source the source object to convert, which must be an instance of {@code S} (never {@code null}) * @return the converted object, which must be an instance of {@code T} (potentially {@code null}) * @throws IllegalArgumentException if the source cannot be converted to the desired target type */ T convert(S source); }
Converter只提供了一個convert方法,其中S表明源類型,T表明目標類型spring
public class TelephoneConverter implements Converter<String, Telephone> { @Override public Telephone convert(String source) { if (source.matches("\\d{3,4}-\\d{7,8}")) { String[] telephoneArray = source.split("-"); return new Telephone(telephoneArray[0], telephoneArray[1]); } return null; } }
@RequestMapping (value="/converter/1",method= RequestMethod.GET) @ResponseBody public Person demo1(Person p) { return p; }
若是咱們同時配置了PropertyEditor和Converter,spring默認先做用PropertyEditor,再做用Converter,可是最好不要這樣子2個都上。數組
<mvc:annotation-driven conversion-service="myConverterService"></mvc:annotation-driven> <bean id="myConverterService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.kings.template.mvc.TelephoneConverter"></bean> </set> </property> </bean>
在spring-core下的org.springframework.core.convert包下提供了許多內置的convertermvc
標量轉換器app
轉換器類 | 功能 |
---|---|
StringToBooleanConverter | String----->Boolean true: true/on/yes/1;false: false/off/no/0 |
ObjectToStringConverter | Object----->String 調用toString方法轉換 |
StringToNumberConverterFactory | String----->Number(如Integer、Long等) |
NumberToNumberConverterFactory | Number子類型(Integer、Long、Double等)-----> Number子類型(Integer、Long、Double等) |
StringToCharacterConverter | String----->java.lang.Character 取字符串第一個字符 |
NumberToCharacterConverter | Number子類型(Integer、Long、Double等)-----> java.lang.Character |
CharacterToNumberFactory | java.lang.Character ----->Number子類型(Integer、Long、Double等) |
StringToEnumConverterFactory | String----->enum類型 |
EnumToStringConverter | enum類型----->String 返回enum對象的name()值 |
StringToLocaleConverter | String----->java.util.Local |
PropertiesToStringConverter | java.util.Properties----->String |
StringToPropertiesConverter | String----->java.util.Properties |
集合、數組相關轉換器框架
轉換器類 | 功能 |
---|---|
ArrayToCollectionConverter | 任意S數組---->任意T集合(List、Set) |
CollectionToArrayConverter | 任意T集合(List、Set)---->任意S數組 |
ArrayToArrayConverter | 任意S數組<---->任意T數組 |
CollectionToCollectionConverter | 任意T集合(List、Set)<---->任意T集合(List、Set) |
MapToMapConverter | Map<---->Map之間的轉換 |
ArrayToStringConverter | 任意S數組---->String類型 |
StringToArrayConverter | String----->數組 默認經過「,」分割,且去除字符串的兩邊空格(trim) |
ArrayToObjectConverter | 任意S數組---->任意Object的轉換 |
ObjectToArrayConverter | Object----->單元素數組 |
CollectionToStringConverter | 任意T集合(List、Set)---->String類型 |
StringToCollectionConverter | String----->集合(List、Set) |
CollectionToObjectConverter | 任意T集合---->任意Object的轉換 |
ObjectToCollectionConverter | Object----->單元素集合 |
默認(fallback)轉換器以前的轉換器不能轉換時調用ide
轉換器類 | 功能 |
---|---|
ObjectToObjectConverter | Object(S)----->Object(T)首先嚐試valueOf進行轉換、沒有則嘗試new 構造器(S) |
IdToEntityConverter | Id(S)----->Entity(T) |
FallbackObjectToStringConverter | Object----->String 最終轉換方法,調用toString() |
需求:須要將一個類中的String轉換成Enum,並且是有多個,如:工具
@Data public class Person { private String name; private Telephone telephone; private Sex sex; private Race race;
那麼咱們再經過寫一個SexConverter和RaceConverter是能夠實現,可是咱們固然有更懶的實現方法。測試
經過工廠方法,抽象出將String轉化成枚舉的過程,省去每次再去定義一個方法去實現相似業務的轉化器。
public interface ConverterFactory<S, R> { /** * Get the converter to convert from S to target type T, where T is also an instance of R. * @param <T> the target type * @param targetType the target type to convert to * @return A converter from S to T */ <T extends R> Converter<S, T> getConverter(Class<T> targetType); }
咱們來一個覆蓋spring默認的StringConvertorFactory
/** * <p class="detail"> * 功能:將枚舉的數字轉化成枚舉列 * </p> * @param <T> the type parameter * * @author Kings * @ClassName Convent tag num 2 emum list. * @Version V1.0. * @date 2016.03.24 18:46:21 */ public class ConventNum2Emum<T extends Enum<T>> { /** * <p class="detail"> * 功能:與或轉化 * </p> * @param status :狀態 * @param enumType :枚舉類型 * * @return list * @author Kings * @date 2016.03.24 18:46:21 */ public List<T> convent(Long status,Class enumType) { List<T> tags = new ArrayList<T>(); if(status != null){ Field[] fields = enumType.getFields(); for (Field f : fields) { Enum<T> e = Enum.valueOf(enumType,f.getName()); Long eValue = Long.parseLong(e.toString()); if((eValue & status) == eValue){ tags.add((T) e); } } } return tags; } /** * <p class="detail"> * 功能:非與或轉換 * </p> * @param status :狀態 * @param enumType :枚舉類型 * * @return list * @author Kings * @date 2016.06.15 10:46:35 */ public T conventNormal(Long status, Class enumType) { if (status != null) { Field[] fields = enumType.getFields(); for (Field f : fields) { Enum<T> e = Enum.valueOf(enumType, f.getName()); Long eVlue = Long.parseLong(e.toString()); if (status.longValue() == eVlue.longValue()) { return (T) e; } } } return null; } }
public final class MyStringConverterFactory implements ConverterFactory<String, Enum> { @Override public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) { return new LongToEnum(targetType); } private class LongToEnum<T extends Enum> implements Converter<String, T> { private final Class<T> enumType; public LongToEnum(Class<T> enumType) { this.enumType = enumType; } @Override public T convert(String source) { return (T) new ConventNum2Emum().conventNormal(Long.parseLong(source), enumType); } } }
<mvc:annotation-driven conversion-service="myConverterService"></mvc:annotation-driven> <bean id="myConverterService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.kings.template.mvc.MyStringConverterFactory"></bean> </set> </property> </bean>
ConditionalGenericConverter繼承了2個重要的接口GenericConverter和ConditionConverter
GenericConverter
/** * Return the source and target types that this converter can convert between. * <p>Each entry is a convertible source-to-target type pair. * <p>For {@link ConditionalConverter conditional converters} this method may return * {@code null} to indicate all source-to-target pairs should be considered. */ Set<ConvertiblePair> getConvertibleTypes(); /** * Convert the source object to the targetType described by the {@code TypeDescriptor}. * @param source the source object to convert (may be {@code null}) * @param sourceType the type descriptor of the field we are converting from * @param targetType the type descriptor of the field we are converting to * @return the converted object */ Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
GenericConverter接口是全部的Converter接口中最靈活也是最複雜的一個類型轉換接口。像咱們以前介紹的Converter接口只支持從一個原類型轉換爲一個目標類型;ConverterFactory接口只支持從一個原類型轉換爲一個目標類型對應的子類型;而GenericConverter接口支持在多個不一樣的原類型和目標類型之間進行轉換,這也就是GenericConverter接口靈活和複雜的地方。GenericConverter接口中一共定義了兩個方法,getConvertibleTypes()和convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType)。getConvertibleTypes方法用於返回這個GenericConverter可以轉換的原類型和目標類型的這麼一個組合;convert方法則是用於進行類型轉換的,咱們能夠在這個方法裏面實現咱們本身的轉換邏輯。
爲了方便我就直接用User和Person
@Data public class Person { private String name; private Telephone telephone; private Sex sex; private Race race; private User u; }
public class MyGenericConverter implements GenericConverter { @Autowired private UserService userService; @Override public Set<ConvertiblePair> getConvertibleTypes() { Set<ConvertiblePair> pairs = new HashSet<ConvertiblePair>(); pairs.add(new ConvertiblePair(String.class, User.class)); //受web層的request.getParamater()的影響,在web層Long做用不了 pairs.add(new ConvertiblePair(Long.class, User.class)); return pairs; } @Override public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (source == null) { return null; } if(sourceType.getType() == Long.class){ return userService.selectByPrimaryKey(source); } else if(sourceType.getType() == String.class){ User u4q = new User(); u4q.setName(source+""); return userService.selectOne(u4q); } return null; } public void setUserService(UserService userService) { this.userService = userService; } }
@RequestMapping (value="/converter/2",method= RequestMethod.GET) @ResponseBody public Person demo2(Person p) { return p; }
直接訪問:http://localhost:8080/kingstemplate/converter/2?u=ws
獲得Json結果以下:
{"name":null,"telephone":null,"sex":null,"race":null,"u":{"id":1,"name":"ws","age":26}}
在5.2.3裏面咱們用不了Long轉User,由於受web項目的影響,此次咱們來個非web項目的
首先註冊一個DefaultConversionService,由於它裏面有個addConverter方便我麼本身加入容器中的converter
<bean id="defaultConversionService" class="org.springframework.core.convert.support.DefaultConversionService"></bean>
再上測試demo
public class ConverterDemo { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("config/spring.xml"); UserService userService = (UserService) context.getBean("userServiceImpl"); MyGenericConverter myGenericConverter = new MyGenericConverter(); //注入接口 myGenericConverter.setUserService(userService); DefaultConversionService defaultConversionService = (DefaultConversionService) context.getBean("defaultConversionService"); defaultConversionService.addConverter(myGenericConverter); User u = defaultConversionService.convert(1L, User.class);//Long->User System.out.println(u.getAge());//age輸出不告訴你 User u1 = defaultConversionService.convert("ws", User.class);//String->User System.out.println(u1.getAge());//age輸出不告訴你 } }
通常的ConversionService最底層都會繼承ConverterRegistry和ConversionService
類型轉換器註冊支持,能夠註冊/刪除相應的類型轉換器
註冊的時候添加了:Converter、GenericConverter、ConverterFactory以及移除某些類型的轉換
void addConverter(Converter<?, ?> converter); /** * Add a plain converter to this registry. * The convertible source/target type pair is specified explicitly. * <p>Allows for a Converter to be reused for multiple distinct pairs without * having to create a Converter class for each pair. * @since 3.1 */ <S, T> void addConverter(Class<S> sourceType, Class<T> targetType, Converter<? super S, ? extends T> converter); /** * Add a generic converter to this registry. */ void addConverter(GenericConverter converter); /** * Add a ranged converter factory to this registry. * The convertible source/target type pair is derived from the ConverterFactory's parameterized types. * @throws IllegalArgumentException if the parameterized types could not be resolved. */ void addConverterFactory(ConverterFactory<?, ?> converterFactory); /** * Remove any converters from sourceType to targetType. * @param sourceType the source type * @param targetType the target type */ void removeConvertible(Class<?> sourceType, Class<?> targetType);
提供運行期類型轉換的支持
/** * Return {@code true} if objects of {@code sourceType} can be converted to the {@code targetType}. * <p>If this method returns {@code true}, it means {@link #convert(Object, Class)} is capable * of converting an instance of {@code sourceType} to {@code targetType}. * <p>Special note on collections, arrays, and maps types: * For conversion between collection, array, and map types, this method will return {@code true} * even though a convert invocation may still generate a {@link ConversionException} if the * underlying elements are not convertible. Callers are expected to handle this exceptional case * when working with collections and maps. * @param sourceType the source type to convert from (may be {@code null} if source is {@code null}) * @param targetType the target type to convert to (required) * @return {@code true} if a conversion can be performed, {@code false} if not * @throws IllegalArgumentException if {@code targetType} is {@code null} */ boolean canConvert(Class<?> sourceType, Class<?> targetType); /** * Return {@code true} if objects of {@code sourceType} can be converted to the {@code targetType}. * The TypeDescriptors provide additional context about the source and target locations * where conversion would occur, often object fields or property locations. * <p>If this method returns {@code true}, it means {@link #convert(Object, TypeDescriptor, TypeDescriptor)} * is capable of converting an instance of {@code sourceType} to {@code targetType}. * <p>Special note on collections, arrays, and maps types: * For conversion between collection, array, and map types, this method will return {@code true} * even though a convert invocation may still generate a {@link ConversionException} if the * underlying elements are not convertible. Callers are expected to handle this exceptional case * when working with collections and maps. * @param sourceType context about the source type to convert from * (may be {@code null} if source is {@code null}) * @param targetType context about the target type to convert to (required) * @return {@code true} if a conversion can be performed between the source and target types, * {@code false} if not * @throws IllegalArgumentException if {@code targetType} is {@code null} */ boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType); /** * Convert the given {@code source} to the specified {@code targetType}. * @param source the source object to convert (may be {@code null}) * @param targetType the target type to convert to (required) * @return the converted object, an instance of targetType * @throws ConversionException if a conversion exception occurred * @throws IllegalArgumentException if targetType is {@code null} */ <T> T convert(Object source, Class<T> targetType); /** * Convert the given {@code source} to the specified {@code targetType}. * The TypeDescriptors provide additional context about the source and target locations * where conversion will occur, often object fields or property locations. * @param source the source object to convert (may be {@code null}) * @param sourceType context about the source type to convert from * (may be {@code null} if source is {@code null}) * @param targetType context about the target type to convert to (required) * @return the converted object, an instance of {@link TypeDescriptor#getObjectType() targetType} * @throws ConversionException if a conversion exception occurred * @throws IllegalArgumentException if targetType is {@code null}, * or {@code sourceType} is {@code null} but source is not {@code null} */ Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
Spring提供了兩個默認實現(其都實現了ConverterRegistry、ConversionService接口):
DefaultConversionService:默認的類型轉換服務實現;
DefaultFormattingConversionService:帶數據格式化支持的類型轉換服務實現,通常使用該服務實現便可。