Spring 学习记录3 ConversionService

ConversionService与Environment的关系

通过之前的学习(Spring 学习记录2 Environment),我已经Environment主要是负责解析properties和profile...但是它虽然实现了相关的接口,但是具体工作并不是由它本身处理,而是委托了其他的类来帮忙..properties相关的接口方法最终主要是通过PropertySourcesPropertyResolver这个类来处理的..(它们实现了相同的接口)

Spring 学习记录3 ConversionService_第1张图片Spring 学习记录3 ConversionService_第2张图片

在通过Environment使用properties相关的方法中,有一些方法是带泛型参数的,比如

 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 getProperty(String key, Class targetType);

得到properties以后肯定要通过一些类型转换,才能从String类型得到T类型.那么这个类型转换.其实用的就是ConversionService以及其相关的一套类.

properties文件中的所有值都是String类型的,而java内存里都是对象.所以需要一些工具将String(或者其他类型)转化成我们需要的java类型..(ConversionService是一套通用的转化方案,并不只是在这里用到,任何需要类型转化的地方都可以用)

 

ConversionService

实验1

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 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对象.

追踪断点发现:

Spring 学习记录3 ConversionService_第3张图片

PropertySourcesPropertyResolver内部得到属性值a,b,c,1,2,3以后通过conversionService去convert成List类型.

所以让我们来研究下ConversionService吧

 

ConversionService的结构

public abstract class AbstractPropertyResolver implements ConfigurablePropertyResolver {

    protected final Log logger = LogFactory.getLog(getClass());

    protected ConfigurableConversionService conversionService = new DefaultConversionService();

............

.............

}

conversionService是定义在AbstractPropertyResolver中的.也就是PropertySourcesPropertyResolver的父抽象类中.

Spring 学习记录3 ConversionService_第4张图片

 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      * 

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 *

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 *

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 *

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 convert(Object source, Class 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 }

View Code

查看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 }
View Code

查看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  * 

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 }

View Code

查看DefaultConversionService的代码得知,它的构造方法里添加了一堆Converter,这些converter是Spring已经帮助我们实现的.通过这些Converter我们可以进行很多通用类型的转化.比如之前的string->list的类型转化.

 

Converter接口

/*
 * 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.
 *
 * 

Implementations may additionally implement {@link ConditionalConverter}. * * @author Keith Donald * @since 3.0 * @param The source type * @param The target type */ public interface Converter { /** * 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); }

View Code

Converter接口很简单,就是把S类型转化成T类型.

实验2

利用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()方法...

 

ConverterFactory和GenericConverter

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  * 

Implementations may additionally implement {@link ConditionalConverter}. 23 * 24 * @author Keith Donald 25 * @since 3.0 26 * @see ConditionalConverter 27 * @param the source type converters created by this factory can convert from 28 * @param 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 { 32 33 /** 34 * Get the converter to convert from S to target type T, where T is also an instance of R. 35 * @param the target type 36 * @param targetType the target type to convert to 37 * @return A converter from S to T 38 */ 39 extends R> Converter getConverter(Class targetType); 40 41 }

View Code

看源代码可以发现ConverterFactory更像是1对N的转化.

可以从S->各种R的各种子类型T..因为平时处理业务上面的各种转化基本上都是很特殊的1:1的专门的converter去转化.所以可能ConverterFactory和GenericConverter不太用得到.因此主要看看Spring是怎么用这些Converter的吧.

实验3

 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 extends Number> T parseNumber(String text, Class 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  * 

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 *

This interface should generally not be used when the simpler {@link Converter} or 35 * {@link ConverterFactory} interfaces are sufficient. 36 * 37 *

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 *

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 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 }

View Code

1个GenericConverter支持转化的所有类型都写在了属性Set内.

 

实验四

    /**
     * 测试GenericConverter CollectionToCollectionConverter
     */
    @Test
    public void testConversionService3() {
        List list1 = Arrays.asList(1, 2, 3, 4, 5);
        Set set1 = conversionService.convert(list1, Set.class); // Set
        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  * 

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 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 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 } View Code

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 converters =
2                 new LinkedHashMap(36);

ConvertiblePair是source的class与target的Class的封装

ConvertersForPair内部含有

1 LinkedList converters = new LinkedList();

所以是各种genericConvrter的封装.

因为GenericConverter可以转化N种source->target的配对.所以可以对应N个ConvertiblePair,也就是说N个ConvertiblePair对应的ConvertersForPair中的GenericConverter可以是同一个.(虽然我Spring中好像没有看到这样的..基本都是对应1个ConvertiblePair)

同样,多个GenericConverter也可以转化同一个source->target的配对,所以1个ConvertiblePair对应的ConvertersForPair中可以有多个GenericConverter.(虽然Spring中也很少出现我只发现了1个)

Spring 学习记录3 ConversionService_第5张图片

这样情况下如果要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用来储存所有converter.

1个GenericConverter可以对应N个ConvertiblePair,1个ConvertiblePair对应的ConvertersForPair中也可以有N个GenericConverter.

 

你可能感兴趣的:(Spring 学习记录3 ConversionService)