Spring 自定义 Converter
在上节 Spring 之请求参数解析原理 中有说到关于参数的类型转换是依靠 WebDataBinder(数据绑定器,进行数据绑定的工作)中的 conversionService(负责数据类型的转换和格式化工作 )中的各个converters (负责各种 数据类型的转换 工作)来处理的,这节来说说它~
- 关于Spring的核心元素 DispatchServlet 请查看:Spring 原理之 DispatchServlet 原理
- 关于 Spring 之请求参数解析原理(实体类传参解析)请查看 Spring 之请求参数解析原理(实体类传参解析)
- 关于 Spring 请求参数类型转换解析 请查看 Spring 请求参数类型转换解析
前言
在定义一个接口时,有很多种方式来实现接口的参数接收,常用的有以下三种:
-
request 作为接口的方法参数,然后request 根据 key获取传递的参数值
@GetMapping("/request_getValue") public ResponseEntity<User> requestGetValue(HttpServletRequest request){ // request 根据 key 获取值 String username = request.getParameter("username"); String name = request.getParameter("name"); User user = new User(); user.setName(name); user.setUsername(username); return new ResponseEntity<>(user , HttpStatus.OK); }
-
直接以方法参数的方式进接收传递的参数值
// 直接以参数的形式获取参数值 @GetMapping("/param_getValue") public ResponseEntity<User> paramGetValue(String username, String name){ User user = new User(); user.setName(name); user.setUsername(username); return new ResponseEntity<>(user , HttpStatus.OK); }
-
利用 Pojo 类 以方法参数的方式来封装获取参数值
@GetMapping("/pojo_getValue") public ResponseEntity<User> pojoGetValue(User user){ return new ResponseEntity<>(user , HttpStatus.OK); }
在上述调用接口的过程中,接口中参数的类型 与 调用接口传递数据的数据类型,会涉及到类型转换,而这些类型转换都由 WebDataBinder(数据绑定器,进行数据绑定的工作)中的 conversionService(负责数据类型的转换和格式化工作 )中的各个 converters (负责各种 数据类型的转换 工作)来处理的
converters 默认是一个大小为 124 的HashMap,key 为 转换前类型->转换后类型 字符串,value 为 org.springframework.core.convert.converter.ConverterFactory 的实现类,相互对应完成数据的转换,部分举例如下:
自定义 Converter
如果不想依靠上面的类型转换处理,可以自定义 Converter。在上述类型转换的底层原理是通过调用 ConverterFactory 的实现类(converter HashMap中 key 对应的value值)中 Converter 实现类的 **convert **方法来处理
那么我们通过自定义实现 **ConverterFactory ** 来完成我们自己希望的类型转换,主要涉及三个步骤:
- 自定义 Converter 类,实现 Converter 接口,复写convert() 方法,完成自己的需求
- 注入自定义 Converter 类
以 java.lang.String -> java.time.LocalDate 的转换为例来举例说明:
自定义 Converter 类,实现 Converter 接口
编写 Converter 的实现类 LocalDateConverter 完成 String ====》LocalDate 的转换
package com.study.config;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public final class LocalDateConverter implements Converter<String, LocalDate> {
private final DateTimeFormatter formatter;
/**
* 根据pattern
*
* @param dateFormat
*/
public LocalDateConverter(String dateFormat) {
this.formatter = DateTimeFormatter.ofPattern(dateFormat);
}
/**
* 根据ISO来指定
*
* @param iso
*/
public LocalDateConverter(DateTimeFormat.ISO iso) {
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
switch (iso) {
case DATE:
formatter = DateTimeFormatter.ISO_DATE;
case DATE_TIME:
formatter = DateTimeFormatter.ISO_DATE_TIME;
default:
formatter = DateTimeFormatter.ISO_DATE;
}
this.formatter = formatter;
}
@Override
public LocalDate convert(String source) {
if (source.isEmpty()) {
return null;
}
return LocalDate.parse(source, formatter);
}
}
注入自定义 Converter 类
注入 Formatters ,传入自定义的pattern / iso
package com.study.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
@Configuration
public class MyWebMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addFormatters(FormatterRegistry registry) {
registry.addConverter(new LocalDateConverter("yyyy-MM-dd"));
//或者
//registry.addConverter(new LocalDateConverter(DateTimeFormat.ISO.DATE));
}
}
1、接口编写
@GetMapping("/test_localDate")
public ResponseEntity<User> localDateValue(LocalDate date) {
System.out.println(date);
return new ResponseEntity<>(HttpStatus.OK);
}
2、接口调用(正常调用)
http://localhost:8081/test_localDate?date=2022-11-24
3、原理
上面的解析是通过 org.springframework.format.support.FormattingConversionService$ParserConverter 的convert 方法完成的转换,而这里调试后的convert就为我们上面编写的 LocalDateConverter 完成转换,即改变实现类型转换的convert来实现具体的业务