客户端传递枚举类型参数
概述,在利用Spring进行Web后台开发时,经常会遇到枚举类型的绑定问题。一般情况下,如果Spring接收到的参数值为枚举值对应的字符串,Spring会根据枚举的值与传入的字符串进行对应。
枚举类如下:
public enum SexEnum { BOY("01","boy","男"), GIRL("02","girl","女") ; private String code; private String type; private String message; private static Map<String,SexEnum> enumMap = Maps.newHashMap(); static{ Stream.of(SexEnum.values()).parallel().forEach(obj -> { enumMap.put(obj.getCode(), obj); }); } private SexEnum(String code, String type, String message) { this.code = code; this.type = type; this.message = message; } //省略get/set方法。。。 public static SexEnum getEnumByCode(String code) { return StringUtils.isBlank(code) ? null : enumMap.get(code); } }
那么,只要客户端在发送请求时,将参数的值设为BOY或GIRL即可。请求类似如下形式:
/** * 使用/guest/getEnumByCode?sex=BOY,传值方式 <br/> */ @GetMapping("/guest/getEnumByCode") public String getEnumByCode(@RequestParam("sex") SexEnum sexEnum){ return sexEnum.getMessage()+":"+sexEnum.getCode(); }
但是,假如客户端传来的参数值不是枚举值对应的字符串,而是诸如01、02编码之类的值,Spring就没法 做自动对应了。这种情况下该如何处理呢?
这时我们需要用org.springframework.core.convert.converter.Converter进行数据绑定,Spring为我们提供了一个类型自动转换接口Converter<S, T>,可以实现从一个Object转为另一个Object的功能。
我们先来看一下Converter接口的定义:
Converter接口定义:
public interface Converter<S, T> { /** * Convert the source object of type {@code S} to target type {@code T}. * @param source the source object to convert, which must be an instance of {@code S} (never {@code null}) * @return the converted object, which must be an instance of {@code T} (potentially {@code null}) * @throws IllegalArgumentException if the source cannot be converted to the desired target type */ T convert(S source); }
第一个类型表示原类型,第二个类型表示目标类型,然后里面定义了一个convert方法,将原类型对象作为参数传入进行转换之后返回目标类型对象。当我们需要建立自己的converter的时候就可以实现该接口。
下面给出一个字符串转换为SexEnum枚举的converter实现。需要注意的是,在Spring MVC和Spring Boot中,由于从客户端接收到的请求都被视为String类型,所以只能用String转枚举的converter。
代码如下:
@SpringBootConfiguration public class CustomConverter implements Converter<String, SexEnum> { @Override public SexEnum convert(String code) { return SexEnum.getEnumByCode(code); } }
客户端在发送请求时,将参数的值设为01或02即可。请求类似如下形式:
/** * 使用/guest/getEnumByCode?sex=01,传值方式 <br/> */ @GetMapping("/guest/getEnumByCode") public String getEnumByCode(@RequestParam("sex") SexEnum sexEnum){ return sexEnum.getMessage()+":"+sexEnum.getCode(); }
但是假如我们的项目工程中有很多枚举类需要进行转换,就需要太多的Converter实现,不太好,怎么办呢?
还好spring除了提供Converter接口之外,还提供了ConverterFactory接口实现类型转换逻辑。
ConverterFactory接口如下:
public interface ConverterFactory<S, R> { /** * Get the converter to convert from S to target type T, where T is also an instance of R. * @param <T> the target type * @param targetType the target type to convert to * @return A converter from S to T */ <T extends R> Converter<S, T> getConverter(Class<T> targetType); }ConverterFactory接口里面就定义了一个产生Converter的getConverter方法,参数是目标类型的class。我们可以看到ConverterFactory中一共用到了三个泛型,S、R、T,其中S表示原类型,R表示目标类型,T是类型R的一个子类。
可以看出,ConverterFactory相比ConvertFactory的好处在于,ConverterFactory可以将原类型转换成一组实现了相同接口类型的对象,而Converter则只能转换成一种类型,假如我们又定义了其他枚举,那么对于每一个枚举,我们都需要实现一个对应的Converter,十分的不方便。而有了ConverterFactory之后,事情就变得简单了不少。现在可以定义一个String到所有实现了BaseEnum的枚举的ConverterFactory,然后根据目标类型生成对应的Converter来进行转换操作。
使用步骤如下:
step1:定义一个公共的枚举类接口BaseEnum,如下:
/** * ClassName:BaseEnum <br/> * Function: 公共枚举类接口 <br/> * Date: 2017年12月25日 下午2:31:52 <br/> * @author shijun@richinfo.cn */ public interface BaseEnum { public String getCode(); }
step2:自定义的枚举类实现该公共枚举类接口
public enum SexEnum implements BaseEnum{ BOY("01","boy","男"), GIRL("02","girl","女") ; private String code; private String type; private String message; private static Map<String,SexEnum> enumMap = Maps.newHashMap(); static{ Stream.of(SexEnum.values()).parallel().forEach(obj -> { enumMap.put(obj.getCode(), obj); }); } private SexEnum(String code, String type, String message) { this.code = code; this.type = type; this.message = message; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public static SexEnum getEnumByCode(String code) { return StringUtils.isBlank(code) ? null : enumMap.get(code); } }
step3:实现ConverterFactory接口,如下:
@SuppressWarnings({"rawtypes","unchecked"}) public class CustomConverterFactory implements ConverterFactory<String, BaseEnum>{ private static final Map<Class, Converter> converterMap = Maps.newHashMap(); @Override public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) { Converter<String, T> converter = converterMap.get(targetType); if(converter == null){ converter = new CodeConverterToEnum<>(targetType); converterMap.put(targetType, converter); } return converter; } /** * * ClassName: StrToEnum <br/> * Function: Spring接收到的参数值为字符串类型,Spring会根据枚举的值与传入的字符串进行对应 <br/> * date: 2017年12月25日 下午3:59:15 <br/> * * @author shijun@richinfo.cn * @version CustomConverterFactory@param <T> * @since V1.0 */ class CodeConverterToEnum<T extends BaseEnum> implements Converter<String, T> { private Map<String, T> enumMap = Maps.newHashMap(); public CodeConverterToEnum(Class<T> enumType) { T[] enums = enumType.getEnumConstants(); for(T e : enums) { enumMap.put(e.getCode(), e); } } @Override public T convert(String source) { return enumMap.get(source); } } }
step4:将实现的CustomConverterFactory加入springboot配置中,如下:
@SpringBootConfiguration public class WebMvcConfigurer extends WebMvcConfigurerAdapter { @Override public void addFormatters(FormatterRegistry registry) { registry.addConverterFactory(customConverterFactory()); } /** * customConverterFactory: 绑定枚举类型参数 <br/> * @author shijun@richinfo.cn */ @Bean public CustomConverterFactory customConverterFactory(){ return new CustomConverterFactory(); } }
做完以上4步,在客户端在发送请求时,将枚举类的code编码做为值传递过来,如code码为01或者02即可,如下
@RestController public class EnumController { /** * 使用/guest/getEnumByCode?sex=01,传值方式 <br/> */ @GetMapping("/guest/getEnumByCode") public String getEnumByCode(@RequestParam("sex") SexEnum sexEnum){ return sexEnum.getMessage()+":"+sexEnum.getCode(); } /** * 使用/guest/getEnumByCodeByPath/01,传值方式 <br/> */ @GetMapping("/guest/getEnumByCodeByPath/{sex}") public String getEnumByCodeByPath(@PathVariable("sex") SexEnum sexEnum){ return sexEnum.getMessage()+":"+sexEnum.getCode(); } /** * 使用/guest/selectByObj?name=abc&age=20&sex=01,传值方式<br/> */ @GetMapping("/guest/selectByObj") public String selectByObj(SimpleRequest simple){ return simple.getSex().getMessage()+":"+simple.getSex().getCode(); } @GetMapping("/guest/listEnums") public Object listEnums(){ return SexEnum.values(); } }
上一篇: SaltStack--数据系统
推荐阅读
-
Java中构造方法、空指针异常现象、基本数据类型和引用数据类型作为参数传递的区别
-
SpringMVC处理参数中的枚举类型通用实现
-
荐 javascript从入门到跑路-----小文的js学习笔记(17)------动态属性、复制变量值、传递参数和检测类型
-
客户端传递枚举类型参数
-
Postman:传递的参数是List类型时 传参格式的写法
-
mybatis中如何传递单个String类型的参数
-
Echarts图表中pie类型tooltip中formatter函数传递的参数从哪里来?
-
web服务器 - .net,java,php这些服务器端的进程是如何获取web客户端传递的参数的?
-
Java的数据类型和参数传递详细介绍
-
Js从头学起(基本数据类型和引用类型的参数传递详细分析)_基础知识