从零开始 Spring Boot 29:类型转换
PropertyEditor
Spring使用PropertyEditor
进行String
和具体类型之间的转换:
public interface PropertyEditor {
void setValue(Object value);
Object getValue();
String getAsText();
void setAsText(String text) throws java.lang.IllegalArgumentException;
// ...
}
这个接口主要有这几个方法:
setValue
,设置修改后的属性值。getValue
,获取属性值。getAsText
,获取属性值对应的字符串。setAsText
,用字符串设置属性值。
PropertyEditor
和之后介绍的ProertyEditorSupport
并不属于Spring框架,这都是java标准包的一部分,属于java.beans
包。这个包定义了java bean相关的组件,更多信息可以阅读java.beans (Java Platform SE 8 ) — beans(Java Platform SE 8) (oracle.com)。
自定义PropertyEditor
时并不需要直接实现PropertyEditor
接口,只需要从PropertyEditorSupport
继承即可:
public class PropertyEditorSupport implements PropertyEditor {
// ...
}
下面具体举例如何在Spring Boot中使用PropertyEditor
进行类型转换。
假设有这么两个实体类:
public class Dog {
private String name;
public Dog(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog(%s)".formatted(name);
}
}
@Data
public class Person {
private String name;
private int age = 0;
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person(name:%s,age:%d)".formatted(name, age);
}
}
我们想直接在Controller中通过传递字符串形式的参数来获取相应的实体类对象:
@RestController
@RequestMapping("/hello")
public class HelloController {
@GetMapping("")
public String hello(@RequestParam Person person, @RequestParam Dog dog){
System.out.println(person);
System.out.println(dog);
return Result.success().toString();
}
}
比如我们希望能处理这样的请求:[localhost:8080/hello?person=tom:11&dog=jerry](http://localhost:8080/hello?person=tom:11&dog=jerry)
。
为了能让Spring将字符串转换为实体类,我们需要为实体类创建对应的PropertyEditor
:
public class PersonEditor extends PropertyEditorSupport {
@Override
public String getAsText() {
Person person = (Person) this.getValue();
return "%s:%d".formatted(person.getName(), person.getAge());
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (text == null || text.isEmpty()) {
throw new IllegalArgumentException("字符串不能为空");
}
int index = text.indexOf(":");
if (index <= 0) {
throw new IllegalArgumentException("缺少:符号");
}
if (text.length() <= index + 1) {
throw new IllegalArgumentException("缺少年龄信息");
}
String name = text.substring(0, index);
String ageText = text.substring(index + 1);
int age;
try {
age = Integer.parseInt(ageText);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("年龄不是整数");
}
setValue(new Person(name, age));
}
}
public class DogEditor extends PropertyEditorSupport {
@Override
public String getAsText() {
Dog dog = (Dog) this.getValue();
return super.getAsText();
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(new Dog(text));
}
}
将自定义
PropertyEditor
命名为xxxEditor
是一种习惯。
主要覆盖getAsText
和setAsText
方法,并实现字符串与具体类型的转换逻辑即可。
只有实体类和对应的ProertyEditor
也是不行的,我们还需要将对应关系注册到Spring中:
@RestController
@RequestMapping("/hello")
public class HelloController {
// ...
@InitBinder
void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Person.class, new PersonEditor());
binder.registerCustomEditor(Dog.class, new DogEditor());
}
}
这样做在HelloController
中,所有请求处理都可以直接将字符串形式的参数转换为对应的实体类对象。
如果将实体类和对应的
PropertyEntity
放在同一个包下面,并且PropertyEntity
类被命名为xxxEditor
,Spring会自动识别检测,不需要手动进行注册。
CustomEditorConfigurer
上面示例中这种通过@InitBinder
注解绑定自定义属性编辑器的做法,只针对当前Controller有效,如果要让Spring框架默认生效,需要使用CustomEditorConfigurer
,这是一个BeanFactoryPostProcessor
,所以可以利用它来改变IoC的行为:
@Configuration
public class AppConfig