經過以前的學習(Spring 學習記錄2 Environment),我已經Environment主要是負責解析properties和profile...可是它雖然實現了相關的接口,可是具體工做並非由它自己處理,而是委託了其餘的類來幫忙..properties相關的接口方法最終主要是經過PropertySourcesPropertyResolver這個類來處理的..(它們實現了相同的接口)html
在經過Environment使用properties相關的方法中,有一些方法是帶泛型參數的,好比java
1 org.springframework.core.env.PropertyResolver 2 3 /** 4 * Return the property value associated with the given key, or {@code null} 5 * if the key cannot be resolved. 6 * @param key the property name to resolve 7 * @param targetType the expected type of the property value 8 * @see #getRequiredProperty(String, Class) 9 */ 10 <T> T getProperty(String key, Class<T> targetType);
獲得properties之後確定要經過一些類型轉換,才能從String類型獲得T類型.那麼這個類型轉換.其實用的就是ConversionService以及其相關的一套類.spring
properties文件中的全部值都是String類型的,而java內存裏都是對象.因此須要一些工具將String(或者其餘類型)轉化成咱們須要的java類型..(ConversionService是一套通用的轉化方案,並不僅是在這裏用到,任何須要類型轉化的地方均可以用)express
properties文件 list=a,b,c,1,2,3
1 /** 2 * 測試ConversionService 3 */ 4 @RunWith(SpringJUnit4ClassRunner.class) 5 @ContextConfiguration("classpath:test-application-context.xml") 6 public class PropertySourcesPropertyResolverTest implements EnvironmentAware { 7 8 StandardEnvironment standardEnvironment; 9 10 @Test 11 public void testPropertySourcesPropertyResolver() { 12 List<String> list = standardEnvironment.getProperty("list", List.class); 13 System.out.println(list); // [a, b, c, 1, 2, 3] 14 } 15 16 @Override 17 public void setEnvironment(Environment environment) { 18 standardEnvironment = (StandardEnvironment) environment; 19 } 20 }
經過Environment的相關properties方法獲取屬性值並轉化成List對象.apache
追蹤斷點發現:數組
PropertySourcesPropertyResolver內部獲得屬性值a,b,c,1,2,3之後經過conversionService去convert成List類型.app
因此讓咱們來研究下ConversionService吧less
public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver { protected final Log logger = LogFactory.getLog(getClass()); protected ConfigurableConversionService conversionService = new DefaultConversionService(); ............ ............. }
conversionService是定義在AbstractPropertyResolver中的.也就是PropertySourcesPropertyResolver的父抽象類中.ide
1 /* 2 * Copyright 2002-2013 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.core.convert; 18 19 /** 20 * A service interface for type conversion. This is the entry point into the convert system. 21 * Call {@link #convert(Object, Class)} to perform a thread-safe type conversion using this system. 22 * 23 * @author Keith Donald 24 * @author Phillip Webb 25 * @since 3.0 26 */ 27 public interface ConversionService { 28 29 /** 30 * Return {@code true} if objects of {@code sourceType} can be converted to the {@code targetType}. 31 * <p>If this method returns {@code true}, it means {@link #convert(Object, Class)} is capable 32 * of converting an instance of {@code sourceType} to {@code targetType}. 33 * <p>Special note on collections, arrays, and maps types: 34 * For conversion between collection, array, and map types, this method will return {@code true} 35 * even though a convert invocation may still generate a {@link ConversionException} if the 36 * underlying elements are not convertible. Callers are expected to handle this exceptional case 37 * when working with collections and maps. 38 * @param sourceType the source type to convert from (may be {@code null} if source is {@code null}) 39 * @param targetType the target type to convert to (required) 40 * @return {@code true} if a conversion can be performed, {@code false} if not 41 * @throws IllegalArgumentException if {@code targetType} is {@code null} 42 */ 43 boolean canConvert(Class<?> sourceType, Class<?> targetType); 44 45 /** 46 * Return {@code true} if objects of {@code sourceType} can be converted to the {@code targetType}. 47 * The TypeDescriptors provide additional context about the source and target locations 48 * where conversion would occur, often object fields or property locations. 49 * <p>If this method returns {@code true}, it means {@link #convert(Object, TypeDescriptor, TypeDescriptor)} 50 * is capable of converting an instance of {@code sourceType} to {@code targetType}. 51 * <p>Special note on collections, arrays, and maps types: 52 * For conversion between collection, array, and map types, this method will return {@code true} 53 * even though a convert invocation may still generate a {@link ConversionException} if the 54 * underlying elements are not convertible. Callers are expected to handle this exceptional case 55 * when working with collections and maps. 56 * @param sourceType context about the source type to convert from 57 * (may be {@code null} if source is {@code null}) 58 * @param targetType context about the target type to convert to (required) 59 * @return {@code true} if a conversion can be performed between the source and target types, 60 * {@code false} if not 61 * @throws IllegalArgumentException if {@code targetType} is {@code null} 62 */ 63 boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType); 64 65 /** 66 * Convert the given {@code source} to the specified {@code targetType}. 67 * @param source the source object to convert (may be null) 68 * @param targetType the target type to convert to (required) 69 * @return the converted object, an instance of targetType 70 * @throws ConversionException if a conversion exception occurred 71 * @throws IllegalArgumentException if targetType is null 72 */ 73 <T> T convert(Object source, Class<T> targetType); 74 75 /** 76 * Convert the given {@code source} to the specified {@code targetType}. 77 * The TypeDescriptors provide additional context about the source and target locations 78 * where conversion will occur, often object fields or property locations. 79 * @param source the source object to convert (may be null) 80 * @param sourceType context about the source type to convert from 81 * (may be {@code null} if source is {@code null}) 82 * @param targetType context about the target type to convert to (required) 83 * @return the converted object, an instance of {@link TypeDescriptor#getObjectType() targetType} 84 * @throws ConversionException if a conversion exception occurred 85 * @throws IllegalArgumentException if targetType is {@code null}, 86 * or {@code sourceType} is {@code null} but source is not {@code null} 87 */ 88 Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); 89 90 }
查看ConversionService接口裏的方法得知,這個類主要就是判斷是否可以類型轉化,能夠的話就轉化.工具
1 /* 2 * Copyright 2002-2009 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.core.convert.converter; 18 19 /** 20 * For registering converters with a type conversion system. 21 * 22 * @author Keith Donald 23 * @author Juergen Hoeller 24 * @since 3.0 25 */ 26 public interface ConverterRegistry { 27 28 /** 29 * Add a plain converter to this registry. 30 * The convertible sourceType/targetType pair is derived from the Converter's parameterized types. 31 * @throws IllegalArgumentException if the parameterized types could not be resolved 32 */ 33 void addConverter(Converter<?, ?> converter); 34 35 /** 36 * Add a plain converter to this registry. 37 * The convertible sourceType/targetType pair is specified explicitly. 38 * Allows for a Converter to be reused for multiple distinct pairs without having to create a Converter class for each pair. 39 * @since 3.1 40 */ 41 void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter); 42 43 /** 44 * Add a generic converter to this registry. 45 */ 46 void addConverter(GenericConverter converter); 47 48 /** 49 * Add a ranged converter factory to this registry. 50 * The convertible sourceType/rangeType pair is derived from the ConverterFactory's parameterized types. 51 * @throws IllegalArgumentException if the parameterized types could not be resolved. 52 */ 53 void addConverterFactory(ConverterFactory<?, ?> converterFactory); 54 55 /** 56 * Remove any converters from sourceType to targetType. 57 * @param sourceType the source type 58 * @param targetType the target type 59 */ 60 void removeConvertible(Class<?> sourceType, Class<?> targetType); 61 62 }
查看ConverterRegistry接口裏的方法得知,這個類主要就是增長Converter用的.
那麼既實現了ConversionService又實現了ConverterRegistry的DefaultConversionService用處就是
1.容許添加類型轉化器Converter.
2.容許調用相關方法進行類型轉化.
1 /* 2 * Copyright 2002-2013 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.core.convert.support; 18 19 import java.util.Locale; 20 import java.util.UUID; 21 22 import org.springframework.core.convert.ConversionService; 23 import org.springframework.core.convert.converter.ConverterRegistry; 24 import org.springframework.util.ClassUtils; 25 26 /** 27 * A specialization of {@link GenericConversionService} configured by default with 28 * converters appropriate for most environments. 29 * 30 * <p>Designed for direct instantiation but also exposes the static 31 * {@link #addDefaultConverters(ConverterRegistry)} utility method for ad hoc use against any 32 * {@code ConverterRegistry} instance. 33 * 34 * @author Chris Beams 35 * @author Juergen Hoeller 36 * @since 3.1 37 */ 38 public class DefaultConversionService extends GenericConversionService { 39 40 /** Java 8's java.util.Optional class available? */ 41 private static final boolean javaUtilOptionalClassAvailable = 42 ClassUtils.isPresent("java.util.Optional", DefaultConversionService.class.getClassLoader()); 43 44 /** Java 8's java.time package available? */ 45 private static final boolean jsr310Available = 46 ClassUtils.isPresent("java.time.ZoneId", DefaultConversionService.class.getClassLoader()); 47 48 49 /** 50 * Create a new {@code DefaultConversionService} with the set of 51 * {@linkplain DefaultConversionService#addDefaultConverters(ConverterRegistry) default converters}. 52 */ 53 public DefaultConversionService() { 54 addDefaultConverters(this); 55 } 56 57 58 // static utility methods 59 60 /** 61 * Add converters appropriate for most environments. 62 * @param converterRegistry the registry of converters to add to (must also be castable to ConversionService, 63 * e.g. being a {@link ConfigurableConversionService}) 64 * @throws ClassCastException if the given ConverterRegistry could not be cast to a ConversionService 65 */ 66 public static void addDefaultConverters(ConverterRegistry converterRegistry) { 67 addScalarConverters(converterRegistry); 68 addCollectionConverters(converterRegistry); 69 70 converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry)); 71 if (jsr310Available) { 72 Jsr310ConverterRegistrar.registerZoneIdConverters(converterRegistry); 73 } 74 75 converterRegistry.addConverter(new ObjectToObjectConverter()); 76 converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry)); 77 converterRegistry.addConverter(new FallbackObjectToStringConverter()); 78 if (javaUtilOptionalClassAvailable) { 79 converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry)); 80 } 81 } 82 83 // internal helpers 84 85 private static void addScalarConverters(ConverterRegistry converterRegistry) { 86 converterRegistry.addConverterFactory(new NumberToNumberConverterFactory()); 87 88 converterRegistry.addConverterFactory(new StringToNumberConverterFactory()); 89 converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter()); 90 91 converterRegistry.addConverter(new StringToCharacterConverter()); 92 converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter()); 93 94 converterRegistry.addConverter(new NumberToCharacterConverter()); 95 converterRegistry.addConverterFactory(new CharacterToNumberFactory()); 96 97 converterRegistry.addConverter(new StringToBooleanConverter()); 98 converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter()); 99 100 converterRegistry.addConverterFactory(new StringToEnumConverterFactory()); 101 converterRegistry.addConverter(Enum.class, String.class, 102 new EnumToStringConverter((ConversionService) converterRegistry)); 103 104 converterRegistry.addConverter(new StringToLocaleConverter()); 105 converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter()); 106 107 converterRegistry.addConverter(new StringToPropertiesConverter()); 108 converterRegistry.addConverter(new PropertiesToStringConverter()); 109 110 converterRegistry.addConverter(new StringToUUIDConverter()); 111 converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter()); 112 } 113 114 private static void addCollectionConverters(ConverterRegistry converterRegistry) { 115 ConversionService conversionService = (ConversionService) converterRegistry; 116 117 converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService)); 118 converterRegistry.addConverter(new CollectionToArrayConverter(conversionService)); 119 120 converterRegistry.addConverter(new ArrayToArrayConverter(conversionService)); 121 converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService)); 122 converterRegistry.addConverter(new MapToMapConverter(conversionService)); 123 124 converterRegistry.addConverter(new ArrayToStringConverter(conversionService)); 125 converterRegistry.addConverter(new StringToArrayConverter(conversionService)); 126 127 converterRegistry.addConverter(new ArrayToObjectConverter(conversionService)); 128 converterRegistry.addConverter(new ObjectToArrayConverter(conversionService)); 129 130 converterRegistry.addConverter(new CollectionToStringConverter(conversionService)); 131 converterRegistry.addConverter(new StringToCollectionConverter(conversionService)); 132 133 converterRegistry.addConverter(new CollectionToObjectConverter(conversionService)); 134 converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService)); 135 } 136 137 138 /** 139 * Inner class to avoid a hard-coded dependency on Java 8's {@code java.time} package. 140 */ 141 private static final class Jsr310ConverterRegistrar { 142 143 public static void registerZoneIdConverters(ConverterRegistry converterRegistry) { 144 converterRegistry.addConverter(new ZoneIdToTimeZoneConverter()); 145 converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter()); 146 } 147 } 148 149 }
查看DefaultConversionService的代碼得知,它的構造方法裏添加了一堆Converter,這些converter是Spring已經幫助咱們實現的.經過這些Converter咱們能夠進行不少通用類型的轉化.好比以前的string->list的類型轉化.
/* * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.core.convert.converter; /** * A converter converts a source object of type S to a target of type T. * Implementations of this interface are thread-safe and can be shared. * * <p>Implementations may additionally implement {@link ConditionalConverter}. * * @author Keith Donald * @since 3.0 * @param <S> The source type * @param <T> The target type */ public interface Converter<S, T> { /** * Convert the source of type S to target type T. * @param source the source object to convert, which must be an instance of S (never {@code null}) * @return the converted object, which must be an instance of T (potentially {@code null}) * @throws IllegalArgumentException if the source could not be converted to the desired target type */ T convert(S source); }
Converter接口很簡單,就是把S類型轉化成T類型.
利用ConversionService進行類型轉化
@Test public void testConversionService1() { String s = conversionService.convert(false, String.class); System.out.println(s); // false Boolean b = conversionService.convert("true", Boolean.class); System.out.println(b); // true } @Before public void setup() { conversionService = standardEnvironment.getConversionService(); }
boolean -> string 用到的是ObjectToStringConverter
string -> boolean 用到的是StringToBooleanConverter
這些都是內置的.同時咱們也能夠發現1個converter也能夠進行N種轉化.由於ObjectToStringConverter不止能夠轉化String.任何類型轉化成String均可以用這個Converter..內部是直接調用toString()方法...
Converter接口在絕大多數狀況下可能都是專門進行S->T類型的轉化.也就是1對1的.Spring還提供了一些其餘接口來幫咱們進行類型轉化.好比ConverterFactory和GenericConverter
1 /* 2 * Copyright 2002-2015 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.core.convert.converter; 18 19 /** 20 * A factory for "ranged" converters that can convert objects from S to subtypes of R. 21 * 22 * <p>Implementations may additionally implement {@link ConditionalConverter}. 23 * 24 * @author Keith Donald 25 * @since 3.0 26 * @see ConditionalConverter 27 * @param <S> the source type converters created by this factory can convert from 28 * @param <R> the target range (or base) type converters created by this factory can convert to; 29 * for example {@link Number} for a set of number subtypes. 30 */ 31 public interface ConverterFactory<S, R> { 32 33 /** 34 * Get the converter to convert from S to target type T, where T is also an instance of R. 35 * @param <T> the target type 36 * @param targetType the target type to convert to 37 * @return A converter from S to T 38 */ 39 <T extends R> Converter<S, T> getConverter(Class<T> targetType); 40 41 }
看源代碼能夠發現ConverterFactory更像是1對N的轉化.
能夠從S->各類R的各類子類型T..由於平時處理業務上面的各類轉化基本上都是很特殊的1:1的專門的converter去轉化.因此可能ConverterFactory和GenericConverter不太用獲得.所以主要看看Spring是怎麼用這些Converter的吧.
1 /** 2 * 測試ConverterFactory StringToNumberConverterFactory 3 */ 4 @Test 5 public void testConversionService2() { 6 double d = conversionService.convert("1.2", double.class); 7 System.out.println(d); //1.2 8 9 int i = conversionService.convert("2", int.class); 10 System.out.println(i); //2 11 12 Byte b = conversionService.convert("0x10", Byte.class); 13 System.out.println(Integer.toBinaryString(b)); //10000 14 }
這裏用到了StringToNumberConverterFactory把String轉化成了Number的各個子類型.
1 @Override 2 public T convert(String source) { 3 if (source.length() == 0) { 4 return null; 5 } 6 return NumberUtils.parseNumber(source, this.targetType); 7 }
StringToNumberConverterFactory經過NumberUtils的static方法進行轉化
1 public static <T extends Number> T parseNumber(String text, Class<T> targetClass) { 2 Assert.notNull(text, "Text must not be null"); 3 Assert.notNull(targetClass, "Target class must not be null"); 4 String trimmed = StringUtils.trimAllWhitespace(text); 5 6 if (targetClass.equals(Byte.class)) { 7 return (T) (isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed)); 8 } 9 else if (targetClass.equals(Short.class)) { 10 return (T) (isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed)); 11 } 12 else if (targetClass.equals(Integer.class)) { 13 return (T) (isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed)); 14 } 15 else if (targetClass.equals(Long.class)) { 16 return (T) (isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed)); 17 } 18 else if (targetClass.equals(BigInteger.class)) { 19 return (T) (isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed)); 20 } 21 else if (targetClass.equals(Float.class)) { 22 return (T) Float.valueOf(trimmed); 23 } 24 else if (targetClass.equals(Double.class)) { 25 return (T) Double.valueOf(trimmed); 26 } 27 else if (targetClass.equals(BigDecimal.class) || targetClass.equals(Number.class)) { 28 return (T) new BigDecimal(trimmed); 29 } 30 else { 31 throw new IllegalArgumentException( 32 "Cannot convert String [" + text + "] to target class [" + targetClass.getName() + "]"); 33 } 34 }
parseNumber方法裏面各類ifelse判斷須要的是哪一種類型的Number而後再轉化.
同理,GenericConverter應該是N:N的轉化
1 /* 2 * Copyright 2002-2015 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.core.convert.converter; 18 19 import java.util.Set; 20 21 import org.springframework.core.convert.TypeDescriptor; 22 import org.springframework.util.Assert; 23 24 /** 25 * Generic converter interface for converting between two or more types. 26 * 27 * <p>This is the most flexible of the Converter SPI interfaces, but also the most complex. 28 * It is flexible in that a GenericConverter may support converting between multiple source/target 29 * type pairs (see {@link #getConvertibleTypes()}. In addition, GenericConverter implementations 30 * have access to source/target {@link TypeDescriptor field context} during the type conversion 31 * process. This allows for resolving source and target field metadata such as annotations and 32 * generics information, which can be used influence the conversion logic. 33 * 34 * <p>This interface should generally not be used when the simpler {@link Converter} or 35 * {@link ConverterFactory} interfaces are sufficient. 36 * 37 * <p>Implementations may additionally implement {@link ConditionalConverter}. 38 * 39 * @author Keith Donald 40 * @author Juergen Hoeller 41 * @since 3.0 42 * @see TypeDescriptor 43 * @see Converter 44 * @see ConverterFactory 45 * @see ConditionalConverter 46 */ 47 public interface GenericConverter { 48 49 /** 50 * Return the source and target types which this converter can convert between. Each 51 * entry is a convertible source-to-target type pair. 52 * <p>For {@link ConditionalConverter conditional} converters this method may return 53 * {@code null} to indicate all source-to-target pairs should be considered. 54 */ 55 Set<ConvertiblePair> getConvertibleTypes(); 56 57 /** 58 * Convert the source to the targetType described by the TypeDescriptor. 59 * @param source the source object to convert (may be null) 60 * @param sourceType the type descriptor of the field we are converting from 61 * @param targetType the type descriptor of the field we are converting to 62 * @return the converted object 63 */ 64 Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); 65 66 67 /** 68 * Holder for a source-to-target class pair. 69 */ 70 public static final class ConvertiblePair { 71 72 private final Class<?> sourceType; 73 74 private final Class<?> targetType; 75 76 /** 77 * Create a new source-to-target pair. 78 * @param sourceType the source type 79 * @param targetType the target type 80 */ 81 public ConvertiblePair(Class<?> sourceType, Class<?> targetType) { 82 Assert.notNull(sourceType, "Source type must not be null"); 83 Assert.notNull(targetType, "Target type must not be null"); 84 this.sourceType = sourceType; 85 this.targetType = targetType; 86 } 87 88 public Class<?> getSourceType() { 89 return this.sourceType; 90 } 91 92 public Class<?> getTargetType() { 93 return this.targetType; 94 } 95 96 @Override 97 public boolean equals(Object other) { 98 if (this == other) { 99 return true; 100 } 101 if (other == null || other.getClass() != ConvertiblePair.class) { 102 return false; 103 } 104 ConvertiblePair otherPair = (ConvertiblePair) other; 105 return (this.sourceType.equals(otherPair.sourceType) && this.targetType.equals(otherPair.targetType)); 106 } 107 108 @Override 109 public int hashCode() { 110 return (this.sourceType.hashCode() * 31 + this.targetType.hashCode()); 111 } 112 113 @Override 114 public String toString() { 115 return (this.sourceType.getName() + " -> " + this.targetType.getName()); 116 } 117 } 118 119 }
1個GenericConverter支持轉化的全部類型都寫在了屬性Set<ConvertiblePair>內.
/** * 測試GenericConverter CollectionToCollectionConverter */ @Test public void testConversionService3() { List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5); Set<String> set1 = conversionService.convert(list1, Set.class); // Set<Integer> System.out.println(set1); // [1, 2, 3, 4, 5] System.out.println(set1.toArray()[0].getClass()); // class java.lang.Integer }
這裏用到了CollectionToCollectionConverter
1 /* 2 * Copyright 2002-2014 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.core.convert.support; 18 19 import java.util.Collection; 20 import java.util.Collections; 21 import java.util.Set; 22 23 import org.springframework.core.CollectionFactory; 24 import org.springframework.core.convert.ConversionService; 25 import org.springframework.core.convert.TypeDescriptor; 26 import org.springframework.core.convert.converter.ConditionalGenericConverter; 27 28 /** 29 * Converts from a Collection to another Collection. 30 * 31 * <p>First, creates a new Collection of the requested targetType with a size equal to the 32 * size of the source Collection. Then copies each element in the source collection to the 33 * target collection. Will perform an element conversion from the source collection's 34 * parameterized type to the target collection's parameterized type if necessary. 35 * 36 * @author Keith Donald 37 * @author Juergen Hoeller 38 * @since 3.0 39 */ 40 final class CollectionToCollectionConverter implements ConditionalGenericConverter { 41 42 private final ConversionService conversionService; 43 44 45 public CollectionToCollectionConverter(ConversionService conversionService) { 46 this.conversionService = conversionService; 47 } 48 49 50 @Override 51 public Set<ConvertiblePair> getConvertibleTypes() { 52 return Collections.singleton(new ConvertiblePair(Collection.class, Collection.class)); 53 } 54 55 @Override 56 public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { 57 return ConversionUtils.canConvertElements( 58 sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), this.conversionService); 59 } 60 61 @Override 62 public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { 63 if (source == null) { 64 return null; 65 } 66 Collection<?> sourceCollection = (Collection<?>) source; 67 68 // Shortcut if possible... 69 boolean copyRequired = !targetType.getType().isInstance(source); 70 if (!copyRequired && sourceCollection.isEmpty()) { 71 return source; 72 } 73 TypeDescriptor elementDesc = targetType.getElementTypeDescriptor(); 74 if (elementDesc == null && !copyRequired) { 75 return source; 76 } 77 78 // At this point, we need a collection copy in any case, even if just for finding out about element copies... 79 Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), 80 (elementDesc != null ? elementDesc.getType() : null), sourceCollection.size()); 81 82 if (elementDesc == null) { 83 target.addAll(sourceCollection); 84 } 85 else { 86 for (Object sourceElement : sourceCollection) { 87 Object targetElement = this.conversionService.convert(sourceElement, 88 sourceType.elementTypeDescriptor(sourceElement), elementDesc); 89 target.add(targetElement); 90 if (sourceElement != targetElement) { 91 copyRequired = true; 92 } 93 } 94 } 95 96 return (copyRequired ? target : source); 97 } 98 99 }
conveter方法中若是source和target的collection是同一種類型的話是不須要轉化的,直接返回source就OK了.
而後73行是我以爲很奇怪的一個地方
TypeDescriptor elementDesc = targetType.getElementTypeDescriptor();
由於泛型不一樣於數組,數組是協變的,泛型是編譯期的功能,因此這行代碼確定返回的是null....不知道這裏爲何還須要去判斷是不是null....ArrayToCollection和其餘一些converter都有本身的實現,彷佛沒走這個converter因此我這裏也不是很懂何時elementDesc會不是null..看這個樣子只有target是數組類纔有可能,可是這樣的話爲何會出如今CollectionToCollectionConverter中呢?很奇怪....
由於elementDesc是null,因此會進target.addAll(sourceCollection)這行,因此就是簡單的把source的全部元素丟到target中了.由於沒有對元素進行轉化.因此Set之中仍然是Integer類型還不是String.
不過也能夠理解.集合中的類型都不知道怎麼能把每一個元素轉化成相應的其餘類型呢...這是作不到的...這大概也是泛型的缺陷吧....
3種不一樣的converter在GenericConversionService類中都有對應的addConverter方法能夠添加converter.經過ConverterAdapter或者ConverterFactoryAdapter最後都會轉化成GenericConverter我想應該是由於這種converter是最通用的緣由吧.
這些適配的GenericConverter會被添加到GenericConversionService的靜態內部類Converters中,而不是List或者Map中去.多是由於查找對應Converter方法的時候比較麻煩.
Converters中有屬性converters
1 Map<ConvertiblePair, ConvertersForPair> converters = 2 new LinkedHashMap<ConvertiblePair, ConvertersForPair>(36);
ConvertiblePair是source的class與target的Class的封裝
ConvertersForPair內部含有
1 LinkedList<GenericConverter> converters = new LinkedList<GenericConverter>();
因此是各類genericConvrter的封裝.
由於GenericConverter能夠轉化N種source->target的配對.因此能夠對應N個ConvertiblePair,也就是說N個ConvertiblePair對應的ConvertersForPair中的GenericConverter能夠是同一個.(雖然我Spring中好像沒有看到這樣的..基本都是對應1個ConvertiblePair)
一樣,多個GenericConverter也能夠轉化同一個source->target的配對,因此1個ConvertiblePair對應的ConvertersForPair中能夠有多個GenericConverter.(雖然Spring中也不多出現我只發現了1個)
這樣狀況下若是要convert source->target是會使用前面的那個converter的...每次添加converter的時候都是向linkledlist調用addFirst方法..因此後面加的應該會放到最前面.
1.Spring使用ConversionService來convert各類類型.默認提供的是DefaultConversionService.同時它實現了ConverterRegistry接口,因此也能夠添加你自定義的converter.
2.Spring提供了3種converter接口,分別是Converter,ConverterFactory和GenericConverter.通常用於1:1, 1:N, N:N的source->target類型轉化.
3.在DefaultConversionService內部3種converter都會轉化成GenericConverter放到靜態內部類Converters中.
4.接口ConvertiblePair是source的class與target的Class的封裝.靜態內部類ConvertersForPair是多個converter對應的LinkedList的封裝..靜態內部類Converters中含有1個Map<ConvertiblePair, ConvertersForPair>用來儲存全部converter.
1個GenericConverter能夠對應N個ConvertiblePair,1個ConvertiblePair對應的ConvertersForPair中也能夠有N個GenericConverter.