环境:SpringBoot3.2.5
1. 简介
在Spring MVC中,数据类型的转换、自动绑定和格式化是一个非常强大的功能,Spring内置了非常多的数据类型转换器。如在一个HTTP请求中SpringMVC默认就具备将JSON个数的数据转换为Java对象,将一个字符串数字转换为Number类型等等。然而,Spring的默认数据绑定机制有时可能无法满足特定的业务需求,比如从特定格式的字符串中解析出自定义对象的实例。为了解决这个问题,Spring允许我们自定义注解和数据格式化器,以便在请求参数和Java对象之间进行自定义的转换。
本篇文章将结合如下需求讲解如何基于SpringBoot环境下自定义注解来实现数据的转换。
现有如下接口:
@GetMapping("/user")
public User getUser(User user) {
return user ;
}
请求url如下:
http://localhost:9001/api/objects/user?user=666,中国
在默认情况下,Spring是无法将这里的user参数值正确的绑定到User对象。
2. 实战案例
2.1 实现目标
为了尽可能的简单,期望通过在接口请求参数上添加一个注解就能完成数据类型的转换及绑定。如下形式:
@GetMapping("/user")
public User getUser(@UserFormat User user)
Spring提供了一种基于注解驱动的格式化,也就是上面这里看到的通过注解标注一个参数(字段)来实现数据的格式化。
要实现基于注解驱动的格式化,需要我们自定义类实现AnnotationFormatterFactory接口。该接口定义如下:
// 这里的泛型是注解类型,也就是我们要使用什么注解来标记我们的参数(字段)
public interface AnnotationFormatterFactory<A extends Annotation> {
// 这个注解可以使用在什么字段上
Set<Class<?>> getFieldTypes();
// 将对象转换为String
Printer<?> getPrinter(A annotation, Class<?> fieldType);
// 将字符串解析为对象
Parser<?> getParser(A annotation, Class<?> fieldType);
}
该接口非常的简单,主要就是如何将对象转String,如何从String转对象。
2.2 自定义注解格式化工厂
public class StringToUserFormatter implements AnnotationFormatterFactory<UserFormat> {
@Override
public Set<Class<?>> getFieldTypes() {
return Set.of(User.class) ;
}
@Override
public Printer<User> getPrinter(UserFormat annotation, Class<?> fieldType) {
return (object, locale) -> object.toString() ;
}
@Override
public Parser<User> getParser(UserFormat annotation, Class<?> fieldType) {
return (text, locale) -> {
Assert.hasText(text, "数据错误") ;
String[] s = text.split(",") ;
User user = new User() ;
user.setId(Long.parseLong(s[0])) ;
user.setName(s[1]) ;
return user ;
} ;
}
}
自定义注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public static @interface UserFormat {
}
有了以上的工厂类后,接下来是注册到Spring容器中。
2.3 注册格式化器
@Component
public class WebDataTypeConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatterForFieldAnnotation(new StringToUserFormatter()) ;
}
}
完成以上步骤就大功告成了,接下来测试
2.4 测试
@GetMapping("/user")
public User getUser(@UserFormat User user) {
return user ;
}
测试结果
图片
正确的转换为User对象。基于该注解进行格式化不仅仅只用到接口参数上,还可以用到字段上。
2.5 用在字段上
public static class DTO {
@UserFormat
private User user ;
private Integer age ;
}
测试接口:
// 注意,可不要改成post,然后用@RequestBody。无用反而报错
@GetMapping("/dto")
public DTO save(DTO dto) {
return dto ;
}
测试结果
图片
正确的输出结果。