一、概述
在利用Spring进行Web后台开发时,经常会遇到枚举类型绑定问题。一般情况下,如果Spring接收到的参数值为字符串类型,Spring会根据枚举的值与传入的字符串进行对应。假设如下枚举
枚举定义
public enum TestEnum {
FIRST(1,"第一个"),
SECOND(2,"第二个"),
THIRD(3,"第三个");
}
那么客户端请求时,将参数值设置成FIRST或SECOND。但是如果设置的字符串不是对应的就没办法完成映射。
二、枚举与Jackson结合
枚举的全部定义
public enum TestEnum {
FIRST(1,"第一个"),
SECOND(2,"第二个"),
THIRD(3,"第三个");
private Integer code;
private String desc;
TestEnum(Integer code,String desc){
this.code = code;
this.desc = desc;
}
@Override
@JsonValue
public String toString() {
return this.getCode().toString();
}
}
通过重写toString方法,即可在转JSON的时候返回code的String值,而不是FIRST这种字符串。通过在toString方法上注解@JsonValue,可在JSON字符串转对象时将code的值转成枚举,但是此方法只限于@RequestBody,返回值通过JSON格式返回时。如果是普通的@RequestParam将达不到效果。
三、Spring转化器实现
1、定义枚举接口
public interface BaseEnum {
Integer getCode();
}
2、定义ConverterFactory
public class CommonEnumConverterFactory implements ConverterFactory<String, BaseEnum> {
private static final Map<Class,Converter> convertMap = new WeakHashMap<>();
@Override
public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
Converter result = convertMap.get(targetType);
if(result == null){
result = new IntegerStrToEnum<T>(targetType);
convertMap.put(targetType,result);
}
return result;
}
class IntegerStrToEnum<T extends BaseEnum> implements Converter<String,T>{
private final Class<T> enumType;
private Map<String,T> enumMap = new HashMap<>();
public IntegerStrToEnum(Class<T> enumType){
this.enumType = enumType;
T[] enums = enumType.getEnumConstants();
for(T e : enums){
enumMap.put(e.getCode() + "",e);
}
}
@Override
public T convert(String s) {
T result = enumMap.get(s);
if(result == null){
throw new IllegalArgumentException("No element matches " + s);
}
return result;
}
}
}
3、将转化器工厂注入到WebMvc
@Configuration
public class WebAppConfigurer implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new CommonEnumConverterFactory());
}
}
启动项目后,不管是通过@RequestParam接收参数,还是通过对象包裹接收参数,都可以通过枚举中的code值映射。
@RequestMapping("/test-param")
public TestEnum testParam(@RequestParam("test")TestEnum testEnum){
}
@RequestMapping("/test-object")
public TestRequest testObject(TestRequest request){
}
但是还存在问题,通过@RequestBody接收参数还是没办法做到映射,前端参数传code不生效,默认用的是枚举的original或者枚举的名称做映射。这个时候就可以结合上面的注解@JsonValue,即枚举与JSON转化时通过被注解的方法返回的值进行。
@Override
@JsonValue
public String toString() {
return this.getCode().toString();
}
注解了@JsonValue后,也将枚举类型的返回值统一成code的字符串。至此,不管使用什么方式进行传参,或者将枚举类型返回,都是保持一致的code字符串,从而达成统一。