0%

spring自定义属性编辑器

Spring 自定义属性编辑器:从原理到实战

在 Spring 中,属性编辑器(Property Editor)负责将配置文件中的字符串值转换为对应的数据类型(如将 "2023-10-01" 转换为 Date 对象)。Spring 内置了多种编辑器(如处理集合、日期的编辑器),但在复杂场景下(如自定义对象转换),需要我们实现自定义属性编辑器。本文将详细讲解自定义属性编辑器的实现步骤、底层原理及实战案例。

属性编辑器的核心作用

Spring 在解析 Bean 配置时,XML 或注解中的属性值都是字符串类型(如 <property name="birthday" value="2023-10-01"/> 中的 "2023-10-01"),而目标 Bean 的属性可能是 DateUser 等复杂类型。

属性编辑器的作用是:将字符串形式的属性值转换为目标数据类型,是 Spring 类型转换体系的核心组件。

Spring 内置属性编辑器

Spring 已默认注册了多种常用属性编辑器(通过 BeanWrapperImpl 实现),覆盖大部分基础类型转换:

编辑器类名 功能描述 示例(字符串→目标类型)
CustomDateEditor 字符串与 Date 转换 "2023-10-01"new Date(2023,9,1)
CustomCollectionEditor 字符串与集合(List/Set)转换 "1,2,3"List.of(1,2,3)
CustomMapEditor 字符串与 Map 转换 "name:张三,age:20"Map 对象
ByteArrayPropertyEditor 字符串与字节数组转换 "hello"byte[] {104,101,108,108,111}
ClassEditor 类名与 Class 对象转换 "java.lang.String"String.class

若内置编辑器无法满足需求(如自定义 User 对象转换),则需要自定义属性编辑器。

自定义属性编辑器的实现步骤

自定义属性编辑器需遵循以下步骤,核心是通过 CustomEditorConfigurerBeanFactoryPostProcessor 实现类)向 Spring 注册编辑器。

步骤 1:创建属性编辑器(继承 PropertyEditorSupport

自定义编辑器需继承 java.beans.PropertyEditorSupport,并重写 setAsText 方法(将字符串转换为目标类型)。

示例:将字符串转换为 User 对象

假设 User 类有 idname 两个属性,需要将 "1,张三" 格式的字符串转换为 User 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import java.beans.PropertyEditorSupport;

// 自定义属性编辑器:String → User
public class UserPropertyEditor extends PropertyEditorSupport {
/**
* 将字符串转换为User对象
* @param text 配置文件中的字符串值(如"1,张三")
*/
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (text == null || text.isEmpty()) {
setValue(null);
return;
}
// 解析字符串:按逗号分割为id和name
String[] parts = text.split(",");
if (parts.length != 2) {
throw new IllegalArgumentException("User格式错误,正确格式:id,name(如1,张三)");
}
// 创建User对象
User user = new User();
user.setId(Long.parseLong(parts[0].trim())); // 解析id
user.setName(parts[1].trim()); // 解析name
// 设置转换后的值
setValue(user);
}
}

// User类定义
public class User {
private Long id;
private String name;
// getter和setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}

步骤 2:创建属性编辑器注册器(实现 PropertyEditorRegistrar

通过 PropertyEditorRegistrar 接口的 registerCustomEditors 方法,将自定义编辑器注册到 Spring 容器。

1
2
3
4
5
6
7
8
9
10
11
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;

// 注册器:负责注册自定义属性编辑器
public class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
// 向容器注册编辑器:指定User类型由UserPropertyEditor处理
registry.registerCustomEditor(User.class, new UserPropertyEditor());
}
}

步骤 3:配置 CustomEditorConfigurer 注册编辑器

CustomEditorConfigurer 是 Spring 提供的处理器(BeanFactoryPostProcessor),用于将注册器中的编辑器应用到容器。

方式 1:XML 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- 1. 定义属性编辑器注册器Bean -->
<bean id="customPropertyEditorRegistrar"
class="com.example.CustomPropertyEditorRegistrar"/>

<!-- 2. 配置CustomEditorConfigurer,关联注册器 -->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<!-- propertyEditorRegistrars:接收一个注册器数组 -->
<property name="propertyEditorRegistrars">
<array>
<ref bean="customPropertyEditorRegistrar"/> <!-- 引用注册器 -->
</array>
</property>
</bean>

<!-- 3. 测试Bean:使用自定义编辑器转换属性 -->
<bean id="userService" class="com.example.UserService">
<!-- "1,张三" 将被UserPropertyEditor转换为User对象 -->
<property name="adminUser" value="1,张三"/>
</bean>
</beans>
方式 2:注解配置(Spring 3.0+)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class EditorConfig {
// 1. 定义注册器Bean
@Bean
public CustomPropertyEditorRegistrar customPropertyEditorRegistrar() {
return new CustomPropertyEditorRegistrar();
}

// 2. 配置CustomEditorConfigurer
@Bean
public CustomEditorConfigurer customEditorConfigurer() {
CustomEditorConfigurer configurer = new CustomEditorConfigurer();
// 设置注册器列表
List<PropertyEditorRegistrar> registrars = new ArrayList<>();
registrars.add(customPropertyEditorRegistrar());
configurer.setPropertyEditorRegistrars(registrars.toArray(new PropertyEditorRegistrar[0]));
return configurer;
}

// 3. 测试Bean
@Bean
public UserService userService() {
UserService service = new UserService();
// 此处可通过@Value注解或手动设置属性,实际会被编辑器转换
return service;
}
}

步骤 4:使用自定义编辑器转换的属性

定义 UserService 类,其 adminUser 属性为 User 类型,验证转换是否生效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class UserService {
private User adminUser;

// setter方法(Spring会通过属性编辑器转换后注入)
public void setAdminUser(User adminUser) {
this.adminUser = adminUser;
}

public void printAdmin() {
System.out.println("管理员ID:" + adminUser.getId() + ",姓名:" + adminUser.getName());
}
}

// 测试代码
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.printAdmin(); // 输出:管理员ID:1,姓名:张三
}
}

底层原理:属性编辑器的执行流程

  1. 容器启动阶段CustomEditorConfigurer 作为 BeanFactoryPostProcessor,在 postProcessBeanFactory 方法中调用注册器的 registerCustomEditors,将自定义编辑器注册到 BeanFactory
  2. Bean 实例化阶段:当 Spring 解析 property 标签时,会根据属性类型查找对应的编辑器(如 User 类型对应 UserPropertyEditor);
  3. 类型转换:调用编辑器的 setAsText 方法,将字符串值转换为目标类型(User 对象);
  4. 属性注入:将转换后的对象注入到 Bean 中(如 UserServiceadminUser 属性)。

进阶:处理日期类型的自定义编辑器

Spring 内置的 CustomDateEditor 需手动指定日期格式,若项目中存在固定格式的日期字符串(如 "yyyy-MM-dd"),可自定义日期编辑器简化配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import org.springframework.beans.propertyeditors.CustomDateEditor;
import java.text.SimpleDateFormat;
import java.util.Date;

// 自定义日期编辑器(固定格式:yyyy-MM-dd)
public class CustomDateEditor extends PropertyEditorSupport {
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

@Override
public void setAsText(String text) throws IllegalArgumentException {
try {
Date date = sdf.parse(text);
setValue(date);
} catch (Exception e) {
throw new IllegalArgumentException("日期格式错误,正确格式:yyyy-MM-dd");
}
}
}

// 在注册器中注册
public class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(User.class, new UserPropertyEditor());
registry.registerCustomEditor(Date.class, new CustomDateEditor()); // 注册日期编辑器
}
}

// 使用示例(XML配置)
<bean id="userService" class="com.example.UserService">
<property name="adminUser" value="1,张三"/>
<property name="createDate" value="2023-10-01"/> <!-- 自动转换为Date对象 -->
</bean>

注意事项与最佳实践

  1. 异常处理:在 setAsText 中需处理解析异常(如格式错误),通过 IllegalArgumentException 抛出,Spring 会捕获并提示具体错误;
  2. 编辑器复用:一个编辑器可处理一种类型,若需处理多种类型,需注册多个编辑器;
  3. 优先级:自定义编辑器会覆盖 Spring 内置编辑器(如自定义 Date 编辑器会覆盖默认的 CustomDateEditor);
  4. Spring 3.0+ 替代方案:对于复杂类型转换,推荐使用 ConverterFormatter(基于类型转换服务 ConversionService),功能更强大且支持泛型。

总结

自定义属性编辑器是 Spring 类型转换体系的重要扩展点,核心步骤为:

  1. 继承 PropertyEditorSupport 实现 setAsText 方法(字符串→目标类型转换);
  2. 通过 PropertyEditorRegistrar 注册编辑器;
  3. 配置 CustomEditorConfigurer 使编辑器生效

欢迎关注我的其它发布渠道