目录
数据绑定之类型转换(Type Conversion)
数据绑定之类型转换(Type Conversion)
1.PropertyEditor
在Spring框架3.0之前并没有提供类型转换器,而是使用JDK原生的java.beans.PropertyEditor进行类型转换(提供了PropertyEditor接口的几个实现类)。从3.0版本之后Spring框架提供了对PropertyEditor 的替代。虽然从3.0出现了替代品,但是在目前的Spring Framework 6版本中PropertyEditor依然发挥着余热。所以学习好PropertyEditor也是非常有必要的。我们先来看看JDK原生的PropertyEditor怎么用。
PropertyEditor如果直译叫做“属性编辑器”,但是我们应该叫它“类型转换器”更加贴合。老Java程序员对它比较熟悉,因为它更多的使用场景是结合在AWT中的内容一起使用(Java的AWT目前已经几乎不使用 了)。作用是用户通过GUI输入一个字符串的值,然后通过PropertyEditor进行类型转换,转换为我们想要的类型。
接口中提供的方法
public interface PropertyEditor {
void setValue(Object value);
Object getValue();
boolean isPaintable();
void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box);
String getJavaInitializationString();
String getAsText();
void setAsText(String text) throws java.lang.IllegalArgumentException;
String[] getTags();
java.awt.Component getCustomEditor();
boolean supportsCustomEditor();
void addPropertyChangeListener(PropertyChangeListener listener);
void removePropertyChangeListener(PropertyChangeListener listener);
}
既然目前Java中AWT已经不使用了,为什么说目前Spring6框架还在使用PropertyEditor的扩展呢?其实也很简单:Java GUI中用户在图形界面输入的内容是字符串,需要进行类型转换为Java支持的类型。而 Spring框架支持XML配置文件,XML配置文件里面内容也都是字符串类型,也需要转换为Bean中属性对应的类型,所以原理是想通的。这也是Spring框架为什么使用PropertyEditor。
2003年时Spring刚出现,本着不重复发明轮子,示好Sun公司,所以很多Spring早期的底层机制都是基于JDK的。后面Spring另起炉灶,开始自己出了一系列替代,是因为Spring强大了。 而且在2003年的时候,Java AWT并没有退出历史舞台。但Spring 在实现PropertyEditor也就是简单重写了下getAsText();和setAsText(String text)
PropertyEditor提供了实现类PropertyEditorSupport(非线程安全),当我们想要使用 PropertyEditor,或者对JDK提供的规范扩充都可以继承这个类进行重写(Spring框架对 PropertyEditor扩展就是继承这个类)。
在JDK中SUN规范所有对PropertyEditor的实现都放在了com.sun.beans.editors中。
以IntegerEditor为例。就是把字符串转为数字,转换过程可以实现进制数。
public class IntegerEditor extends NumberEditor {
public void setAsText(String text) throws IllegalArgumentException {
setValue((text == null) ? null : Integer.decode(text));
}
}
Integer的decode方法
public static Integer decode(String nm) throws NumberFormatException {
int radix = 10;
int index = 0;
boolean negative = false;
Integer result;
if (nm.isEmpty())
throw new NumberFormatException("Zero length string");
char firstChar = nm.charAt(0);
if (firstChar == '-') {
negative = true;
index++;
} else if (firstChar == '+')
index++;
if (nm.startsWith("0x", index) || nm.startsWith("0X", index)) {
index += 2;
radix = 16;
}
else if (nm.startsWith("#", index)) {
index ++;
radix = 16;
}
else if (nm.startsWith("0", index) && nm.length() > 1 + index) {
index ++;
radix = 8;
}
if (nm.startsWith("-", index) || nm.startsWith("+", index)) throw new NumberFormatException("Sign character in wrong position");
try {
result = Integer.valueOf(nm.substring(index), radix);
result = negative ? Integer.valueOf(-result.intValue()) : result;
} catch (NumberFormatException e) {
String constant = negative ? ("-" + nm.substring(index)) : nm.substring(index);
result = Integer.valueOf(constant, radix);
}
return result;
}
除了IntegerEditor以外,其他的PropertyEditor都是从String类型,转换为其他类型,所以功能明显不足。
2.Spring框架中类型转换器
从Spring Framework 3.0开始提供了更加强大的内置转换器,分别是:Converter、 ConverterFactory、GenericConverter 三个接口及实现类,但是这些实现类绝大多数都是default修 饰,无法跨包访问。
这三个接口的功能要比PropertyEditor更加强大。可以实现某个类型向另外一个类型转换,不再局限于 String向其他类型转换。
但是Spring框架到目前的Spring Framework 6一直都对两种方式进行支持。这也是Spring框架的特点, 解耦和原生API方式,让我们开发者自己选(为了让开发者更倾向使用解耦方式,一般解耦方式使用起来 都比原生简单)。
Spring框架类型转换经常发生的,只是这个事情由Spring框架完成,程序员不深入底层时,只是知道有这个事情。如果深入了解Spring框架,就知道Spring在进行数据绑定时会帮助我们进行类型转换。
类型转换几个常见场景。
- Xml文件配置的Bean属性都是字符串类型,而Bean属性类型确不仅仅是字符串。这时会用类型转 换。
- @Value注解。properties文件中值都是字符串,获取后希望是某个类型。这时会用类型转换
- Spring MVC接收请求参数。HTTP请求参数都是字符串,在我们接收时希望接收为其他类型, Spring框架在数据绑定时会进行类型转换。
2.1 Converter接口
Converter接口功能比较简单,就是把S类型值转换为T类型。Spring默认提供了很多实现类,都是一些简单数据类型到另一种简单数据类型的转换。
@FunctionalInterface
public interface Converter<S, T> {
/**
* 从S类型转换为T类型
*/
@Nullable
T convert(S source);
/**
* 从Spring 5.3版本出现的默认方法,允许转换后再次使用另一个转换器(after)进行转换后返回
*/
default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after)
{
Assert.notNull(after, "'after' Converter must not be null");
return (S s) -> {
T initialResult = convert(s);
return (initialResult != null ? after.convert(initialResult) : null);
};
}
}
里面提供了大量的实现了,但是这些类的访问权限修饰符都是default,意味着跨包无法访问。
2.2 ConverterFactory
用于把S类型转换为T类型,但是强制要求T的类型必须是R类型子类。具体是什么类型都行,只要是之类中一种就可以。因为接口泛型中R设置为转换后结果类型的父类,所以具体转后后的类型可能是R的子类 1,也可能是R的子类2,所以有人认为这数据1:N的转换方式。
public interface ConverterFactory<S, R> {
/**
* 从S类型值转换为T类型,其中T类型必须继承R类型。
*/
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
2.3 GenericConverter
GenericConverter转换器主要实现N:N的转换。例如
这些接口的实现类因为大部分都是default修饰,意味着是Spring框架自己使用的,如果想要使用可以实 现接口进行自定义实现。
以String转Integer为例,简单使用一下转换器
自定义转换器
@Component
public class MyConverter implements Converter<String,Integer> {
@Override
public Integer convert(String source) {
return Integer.parseInt(source);
}
}
测试结果
// 简写方式,省略@ContextConfiguration
@SpringJUnitConfig(MyConverter.class)
public class ConverterTest {
@Autowired
MyConverter myConverter;
@Test
void test(){
Integer result = myConverter.convert("123");
System.out.println(result);
}
}