zoukankan      html  css  js  c++  java
  • Spring 学习记录3 ConversionService

    ConversionService与Environment的关系

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

    在通过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> T getProperty(String key, Class<T> 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<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对象.

    追踪断点发现:

    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的父抽象类中.

     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 }
    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  * <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 }
    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.
     *
     * <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);
    
    }
    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  * <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 }
    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 <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 }
    View Code

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

  • 相关阅读:
    css-css背景
    css-概述和选择器
    html-补充
    html-示例代码
    html-表格和列表
    html-表单
    html-常用标签
    html- 头部元素
    html-介绍
    SQLAlchemy-对象关系教程ORM-连接,子查询
  • 原文地址:https://www.cnblogs.com/abcwt112/p/7447435.html
Copyright © 2011-2022 走看看