Spring 学习记录3 ConversionService

2024-05-07 01:38

本文主要是介绍Spring 学习记录3 ConversionService,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

随时随地技术实战干货,获取项目源码、学习资料,请关注源代码社区公众号(ydmsq666)

from:https://www.cnblogs.com/abcwt112/p/7447435.html

ConversionService与Environment的关系

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

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

复制代码

 1 org.springframework.core.env.PropertyResolver2 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 resolve7      * @param targetType the expected type of the property value8      * @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  * 测试ConversionService3  */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的父抽象类中.

 View Code

查看ConversionService接口里的方法得知,这个类主要就是判断是否能够类型转化,可以的话就转化.

 View Code

查看ConverterRegistry接口里的方法得知,这个类主要就是增加Converter用的.

那么既实现了ConversionService又实现了ConverterRegistry的DefaultConversionService用处就是

1.允许添加类型转化器Converter.

2.允许调用相关方法进行类型转化.

 View Code

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

Converter接口

 View Code

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

实验2

利用ConversionService进行类型转化

复制代码

    @Testpublic void testConversionService1() {String s = conversionService.convert(false, String.class);System.out.println(s); // falseBoolean b = conversionService.convert("true", Boolean.class);System.out.println(b); // true}@Beforepublic 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

 View Code

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

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

实验3

复制代码

 1     /**2      * 测试ConverterFactory StringToNumberConverterFactory3      */4     @Test5     public void testConversionService2() {6         double d = conversionService.convert("1.2", double.class);7         System.out.println(d); //1.28 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的转化

 View Code

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

实验四

复制代码

    /*** 测试GenericConverter CollectionToCollectionConverter*/@Testpublic 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

 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.

这篇关于Spring 学习记录3 ConversionService的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/965974

相关文章

java设计模式之工厂模式--普通工厂方法模式(Factory Method)

1.普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。 2.先定义一个接口: package com.zhong.pattern.factorymethod;/*** 发送接口* @author admin**/public interface Sender {/*** 发送消息方法* @param msg*/void send(String msg);} 3

Java设计模式之代理模式2-动态代理(jdk实现)

这篇是接着上一篇继续介绍java设计模式之代理模式。下面讲解的是jdk实现动态代理。 1.)首先我们要声明一个动态代理类,实现InvocationHandler接口 package com.zhong.pattern.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/*** 演

java设计模式之代理模式1--静态代理

Java编程的目标是实现现实不能完成的,优化现实能够完成的,是一种虚拟技术。生活中的方方面面都可以虚拟到代码中。代理模式所讲的就是现实生活中的这么一个概念:助手。 代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。 1.)首先新建一个表演的接口 package com.zhong.pattern.proxy;/*** 表演接口* @author admin*

java原型(Prototype)设计模式

原型模式就是讲一个对象作为原型,使用clone()方法来创建新的实例。 public class Prototype implements Cloneable{private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}@Overri

Java中23种设计模式之适配者模式

适配器模式的作用就是在原来的类上提供新功能。 主要可分为3种: 1.类适配:创建新类,继承源类,并实现新接口,例如:     class Adapter extends OldClass implements NewFunc{} 2.对象适配:创建新类持源类的实例,并实现新接口,例如:     class Adapter implements NewFunc { priva

java不依赖临时变量交换两个变量的值

java不依赖临时变量交换两个变量的值 1.简单易懂的实现方式     int a=1,b=2;     int temp = 0;     temp = a;     a = b;     b= temp; 2.算术算法 int a=1,b=2; a = a+b;// a = 1+2  b = a-b;// b = a-b --> b=3-2 -->1 a = a -b;/

Java中的SOLID原则及示例

类是任何Java应用程序的构建块。如果这些区块不强,那么建筑(即应用)将来将面临艰难时期。这实际上意味着,当应用程序范围上升或应用程序在生产或维护中面临某些设计问题时,不那么好的编写会导致非常困难的情况。 另一方面,一组精心设计和编写的类可以加速编码过程的突飞猛进,同时减少错误的数量。 在本教程中,我们将使用 5个最推荐的设计原则的示例来讨论Java中的SOLID原则,在编写类时我们应该记住这

Java比较和交换示例 - CAS算法

Java比较和交换示例 - CAS算法 由Lokesh Gupta | 提起下:多线程 一个Java 5中最好添加的是支持类,如原子操作AtomicInteger,AtomicLong等等。这些课程帮助您最大限度地减少复杂的(非必要)需要多线程的,如增加一些基本的操作代码或递减的值在多个线程之间共享。这些类内部依赖于名为CAS(比较和交换)的算法。在本文中,我将详细讨论这个概念。 1.乐观和

java并发编程之CyclicBarrier(循环栅栏)

package com.zhong;import java.util.concurrent.CyclicBarrier;/*** Cyclic意思是循环,Barrier意思是屏障,那么CyclicBarrier翻译过来就是循环栅栏。* 它是一个同步辅助类,能让一组线程互相等待,* 直到这一组线程都到了一个公共屏障点,各线程才能继续向下执行。因为该屏障能够在释放等待线程后继续重用,所以叫循环屏障。*

Java内存管理 - 垃圾收集算法

我们都知道Java 中垃圾收集器 [GC] 的功能。但只有少数人试图深入了解垃圾收集的工作原理。你不是其中之一,这就是你在这里的原因。 在这个Java内存管理教程中,我们将尝试了解Java垃圾收集的当前算法,我们将了解这些算法的演变。 目录1. Java中的内存管理2.引用计数机制3.标记和清除机制4.停止并复制GC 5.分代停止和复制6.如何提高Java中的内存利用率 1.