SpringBoot優雅定制接口參數格式轉換
環境: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 ;
}
測試結果
圖片
正確的輸出結果。