我们知道,前端传递给后端的参数只能是String类型的文本.
而在后端Controller中,接收的参数类型可以是String,Integer,Boolean,Collection等各种Java支持的类型.
这中间就需要有一个参数转换的过程.
Spring中默认的转换方式,定义在PropertyEditorRegistrySupport类中createDefaultEditors方法内:
/**
* Actually register the default editors for this registry instance.
*/
private void createDefaultEditors() {
this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64);
// Simple editors, without parameterization capabilities.
// The JDK does not contain a default editor for any of these target types.
this.defaultEditors.put(Charset.class, new CharsetEditor());
this.defaultEditors.put(Class.class, new ClassEditor());
this.defaultEditors.put(Class[].class, new ClassArrayEditor());
this.defaultEditors.put(Currency.class, new CurrencyEditor());
this.defaultEditors.put(File.class, new FileEditor());
this.defaultEditors.put(InputStream.class, new InputStreamEditor());
this.defaultEditors.put(InputSource.class, new InputSourceEditor());
this.defaultEditors.put(Locale.class, new LocaleEditor());
this.defaultEditors.put(Pattern.class, new PatternEditor());
this.defaultEditors.put(Properties.class, new PropertiesEditor());
this.defaultEditors.put(Reader.class, new ReaderEditor());
this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
this.defaultEditors.put(URI.class, new URIEditor());
this.defaultEditors.put(URL.class, new URLEditor());
this.defaultEditors.put(UUID.class, new UUIDEditor());
if (zoneIdClass != null) {
this.defaultEditors.put(zoneIdClass, new ZoneIdEditor());
}
// Default instances of collection editors.
// Can be overridden by registering custom instances of those as custom editors.
this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));
// Default editors for primitive arrays.
this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());
// The JDK does not contain a default editor for char!
this.defaultEditors.put(char.class, new CharacterEditor(false));
this.defaultEditors.put(Character.class, new CharacterEditor(true));
// Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
// The JDK does not contain default editors for number wrapper types!
// Override JDK primitive number editors with our own CustomNumberEditor.
this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));
// Only register config value editors if explicitly requested.
if (this.configValueEditorsActive) {
StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
this.defaultEditors.put(String[].class, sae);
this.defaultEditors.put(short[].class, sae);
this.defaultEditors.put(int[].class, sae);
this.defaultEditors.put(long[].class, sae);
}
}
可以看到,每种参数类型对应一个编译器的示例.
而编辑器则继承了PropertyEditorSupport类.
其中有两个重要方法:
- void setAsText(String text) : 由外部编辑器传入一个String类型的文本,配合setValue()方法更新属性值.
- String getAsText() : 将属性对象用一个字符串表示,以便外部的属性编辑器能以可视化的方式显示。缺省返回null,表示该属性不能以字符串表示;
如果我们自己实现一个编辑器,并加载到Spring中,就可以指定某种入参类型的转换规则了.
实现方式
我们需求将前端传入的13位时间戳转换为Date类型,以此为例来看下如何实现自定义参数转换.
1.创建一个继承PropertyEditorSupport的类,并重写setAsText和getAsText方法
package com.lgeek.smarthotel.common.propertyeditors;
import java.beans.PropertyEditorSupport;
import java.util.Date;
import org.springframework.util.StringUtils;
/**
* 将前端传入的时间戳转换为Date类型放入Controller.
*
* @author lxy
* @since 2018.9.12
*/
public class CustomDateTimeEditor extends PropertyEditorSupport {
private boolean allowEmpty=true;
/** 无参构造*/
public CustomDateTimeEditor() {
}
/** allowEmpty=true表示入参可以为null */
public CustomDateTimeEditor(boolean allowEmpty) {
this.allowEmpty = allowEmpty;
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (this.allowEmpty && !StringUtils.hasText(text)) {
// Treat empty String as null value.
setValue(null);
}
else {
try {
//此处实现时间戳与Date的转换
setValue(new Date(Long.parseLong(text)));
}
catch (Exception ex) {
throw new IllegalArgumentException("Could not parse date from long: " + ex.getMessage(), ex);
}
}
}
@Override
public String getAsText() {
Date value = (Date) getValue();
return value != null ? value.toString() : "";
}
}
2.在Controller类中注册自定义的转换器,使用@InitBinder注解:
@RestController
@RequestMapping("/test")
public class OrderController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Date.class, new CustomDateTimeEditor());
}
@RequestMapping("/date_demo")
@ResponseBody
public String dateDemo(Date start, Date end) {
//TODO xxx
}
}
以上简单的2步,就实现了前端文本参数与Controller中入参类型的转换.