https://xkcoding.com/2019/01/30/spring-boot-request-use-enums-params.html
在我们实际开发中,枚举类型应用十分广泛,可以避免在项目中定义大量的『魔法值』变量。但是,在 web 开发中,如何将枚举对象作为请求参数传进 Controller,做到类型自动转换?直接使用 @RequestParam
和 @RequestBody
断然是不够的,这里就需要我们自定义 Converter
来实现类型转化了。
比如一个用户对象,里面的性别属性,我们定义一个枚举类型 Gender
。
GenderEnum.java
/**
*
* 性别枚举
*
*
* @package: com.xkcoding.springboottest.constants.enums
* @description: 性别枚举
* @author: yangkai.shen
* @date: Created in 2019-01-30 11:06
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
@Getter
public enum GenderEnum {
/**
* 男
*/
MALE(0),
/**
* 女
*/
FEMALE(1);
/**
* 性别编码
*/
private Integer code;
GenderEnum(int code) {
this.code = code;
}
}
请求对象封装 QueryRequest
QueryRequest.java
/**
*
* 请求参数
*
*
* @package: com.xkcoding.springboottest.controller.enum_test
* @description: 请求参数
* @author: yangkai.shen
* @date: Created in 2019-01-30 14:02
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
@Data
public class QueryRequest {
private GenderEnum gender;
}
在 Controller 层直接使用如下方式,期望参数自动进行类型转换
EnumTestController.java
/**
*
* 测试参数是枚举类型,自动转换
*
*
* @package: com.xkcoding.springboottest.controller.enum_test
* @description: 测试参数是枚举类型,自动转换
* @author: yangkai.shen
* @date: Created in 2019-01-30 13:56
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
@Slf4j
@RestController
@RequestMapping("/enum")
public class EnumTestController {
@GetMapping("/get")
public Dict testGet(QueryRequest request) {
log.info("【get-request】= {}", JSONUtil.toJsonStr(request));
return Dict.create().set("get-request", request);
}
@PostMapping("/post")
public Dict testPost(@RequestBody QueryRequest request) {
log.info("【post-request】= {}", JSONUtil.toJsonStr(request));
return Dict.create().set("post-request", request);
}
}
这么写的时候,gender 只能接收到 MALE
、FEMALE
这样的参数,除此以外,均会报类型不匹配的错误信息,此时是无法处理 0
、1
这样的参数的。
需求:
MALE
、FEMALE
这样的参数,可以自动转为对应的枚举值;0
、1
这样的参数,也可以自动转为对应的枚举值。此时,本文的『主角』Converter 就可以隆重登场了。
注意,
Converter
是org.springframework.core.convert.converter.Converter
,别导错包了。
IntegerCodeToGenderEnumConverter.java
/**
*
* 编码 -> 性别 转换器
*
*
* @package: com.xkcoding.springboottest.config.convert
* @description: 编码 -> 性别 转换器
* @author: yangkai.shen
* @date: Created in 2019-01-30 11:14
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
public class IntegerCodeToGenderEnumConverter implements Converter {
private Map enumMap = Maps.newHashMap();
public IntegerCodeToGenderEnumConverter() {
for (GenderEnum genderEnum : GenderEnum.values()) {
enumMap.put(genderEnum.getCode(), genderEnum);
}
}
@Override
public GenderEnum convert(Integer source) {
GenderEnum genderEnum = enumMap.get(source);
if (ObjectUtil.isNull(genderEnum)) {
throw new IllegalArgumentException("无法匹配对应的枚举类型");
}
return genderEnum;
}
}
其实这里已经可以实现类型转换了,但是引出另外一个问题,当我们的枚举类特别多的时候,我们就需要写很多个 自定义的 Converter 来满足类型转化。
所以我们不使用上面直接使用 Converter
的这种方法,我们引入 ConverterFactory
来解决这个问题。
以后的枚举类都需要实现这个接口
BaseEnum.java
/**
*
* 通用枚举接口
*
*
* @package: com.xkcoding.springboottest.constants
* @description: 通用枚举接口
* @author: yangkai.shen
* @date: Created in 2019-01-30 11:04
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
public interface BaseEnum {
/**
* 获取枚举编码
*
* @return 编码
*/
Integer getCode();
}
GenderEnum.java
/**
*
* 性别枚举
*
*
* @package: com.xkcoding.springboottest.constants.enums
* @description: 性别枚举
* @author: yangkai.shen
* @date: Created in 2019-01-30 11:06
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
@Getter
public enum GenderEnum implements BaseEnum {
/**
* 男
*/
MALE(0),
/**
* 女
*/
FEMALE(1);
/**
* 性别编码
*/
private Integer code;
GenderEnum(int code) {
this.code = code;
}
}
Integer -> Enum
的 Converter 类IntegerToEnumConverter.java
/**
*
* 枚举编码 -> 枚举 转化器
*
*
* @package: com.xkcoding.springboottest.config.convert
* @description: 枚举编码 -> 枚举 转化器
* @author: yangkai.shen
* @date: Created in 2019-01-30 11:21
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
public class IntegerToEnumConverter implements Converter {
private Map enumMap = Maps.newHashMap();
public IntegerToEnumConverter(Class enumType) {
T[] enums = enumType.getEnumConstants();
for (T e : enums) {
enumMap.put(e.getCode(), e);
}
}
@Override
public T convert(Integer source) {
T t = enumMap.get(source);
if (ObjectUtil.isNull(t)) {
throw new IllegalArgumentException("无法匹配对应的枚举类型");
}
return t;
}
}
IntegerCodeToEnumConverterFactory.java
/**
*
* 编码 -> 枚举 转化器工厂类
*
*
* @package: com.xkcoding.springboottest.config.convert.factory
* @description: 编码 -> 枚举 转化器工厂类
* @author: yangkai.shen
* @date: Created in 2019-01-30 11:09
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
public class IntegerCodeToEnumConverterFactory implements ConverterFactory {
private static final Map CONVERTERS = Maps.newHashMap();
/**
* 获取一个从 Integer 转化为 T 的转换器,T 是一个泛型,有多个实现
*
* @param targetType 转换后的类型
* @return 返回一个转化器
*/
@Override
public Converter getConverter(Class targetType) {
Converter converter = CONVERTERS.get(targetType);
if (converter == null) {
converter = new IntegerToEnumConverter<>(targetType);
CONVERTERS.put(targetType, converter);
}
return converter;
}
}
String -> Enum
的 Converter 类和对应的 ConverterFactory
工厂类因为 Post 请求可以对传入的 json 属性定义类型,但是 Get 请求后台接收到的参数都是 String 类型,因此需要在创建一个 Converter 类
StringToEnumConverter.java
/**
*
* 枚举编码 -> 枚举 转化器
*
*
* @package: com.xkcoding.springboottest.config.convert
* @description: 枚举编码 -> 枚举 转化器
* @author: yangkai.shen
* @date: Created in 2019-01-30 11:21
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
public class StringToEnumConverter implements Converter {
private Map enumMap = Maps.newHashMap();
public StringToEnumConverter(Class enumType) {
T[] enums = enumType.getEnumConstants();
for (T e : enums) {
enumMap.put(e.getCode().toString(), e);
}
}
@Override
public T convert(String source) {
T t = enumMap.get(source);
if (ObjectUtil.isNull(t)) {
throw new IllegalArgumentException("无法匹配对应的枚举类型");
}
return t;
}
}
StringCodeToEnumConverterFactory.java
/**
*
* 编码 -> 枚举 转化器工厂类
*
*
* @package: com.xkcoding.springboottest.config.convert.factory
* @description: 编码 -> 枚举 转化器工厂类
* @author: yangkai.shen
* @date: Created in 2019-01-30 11:09
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
public class StringCodeToEnumConverterFactory implements ConverterFactory {
private static final Map CONVERTERS = Maps.newHashMap();
/**
* 获取一个从 Integer 转化为 T 的转换器,T 是一个泛型,有多个实现
*
* @param targetType 转换后的类型
* @return 返回一个转化器
*/
@Override
public Converter getConverter(Class targetType) {
Converter converter = CONVERTERS.get(targetType);
if (converter == null) {
converter = new StringToEnumConverter<>(targetType);
CONVERTERS.put(targetType, converter);
}
return converter;
}
}
WebMvcConfig.java
/**
*
* MVC通用配置
*
*
* @package: com.xkcoding.springboottest.config
* @description: MVC通用配置
* @author: yangkai.shen
* @date: Created in 2019-01-30 11:33
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 枚举类的转换器工厂 addConverterFactory
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new IntegerCodeToEnumConverterFactory());
registry.addConverterFactory(new StringCodeToEnumConverterFactory());
}
}
这样我们就可以优雅的实现枚举对象参数的自动转换了,并且支持多个类型转换。其中的类型转换失败异常,可以使用全局异常拦截来处理,不会的小伙伴可以参考以前的文章。https://xkcoding.com/2018/08/20/spring-boot-global-exception-handler.html
https://github.com/xkcoding/owntest/tree/master/spring-boot-test
https://blog.csdn.net/alinyua/article/details/86383254
https://hpgary.iteye.com/blog/2426096
https://blog.wuwii.com/springboot-12.html