概述,在利用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(); } }
相关推荐
在 Java 1.5 版本中,Java 又引入了泛型编程(Generic Programming)、类型安全的枚举、不定长参数和自动装/拆箱等语言特性。 Java 不同于一般的编译执行计算机语言和解释执行计算机语言。它首先将源代码编译成二进 ...
参数 传递参数, 整数型, 可空 .子程序 打开网页, 逻辑型, 公开, 打开指定网址(成功返回真,失败返回假) .参数 网址, 文本型, , 欲打开的网页地址 .子程序 弹出光驱, 逻辑型, 公开, 弹出光驱门。 mciSendString .子...
1.5.3 序列化单例和类型安全的枚举 1.5.4 版本管理 1.5.5 为克隆使用序列化 1.6 文件管理 1.7 新I/O 1.7.1 内存映射文件 1.7.2 缓冲区数据结构 1.7.3 文件加锁机制 1.8 正则表达式 第十二章 XML 2.1 XML概述 2.1.1 ...
config对象是在一个Servlet初始化时,JSP引擎向它传递信息用的,此信息包括Servlet初始化时所要用到的参数(通过属性名和属性值构成)以及服务器的有关信息(通过传递一个ServletContext对象) 序号 方 法 说 明 ...
SOAP服务的输入输出参数可以是简单的数据类型或复杂的数据结构,可以由WSDL解析器自动生成或手工定义。预编译器将自动生成序列化/反 序列化这些数据的代码,以便存根例程可以将这些数据以XML的方式编码或解码。 ...
如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。 18、error和exception有什么区别? error 表示恢复...
枚举类型; 静态导入; 格式化输出; 使用ProcessBuilder执行本地命令; 泛型编程; 监控和管理虚拟机;新的线程执行架构; 线程锁; 线程条件; 线程同步装置:semaphore countdownlatch cyclicbarrier exchanger; 17 java与...
6、修正“文本_加密”返回文本传递给“文本_解密”后长度不正确BUG,改为返回字节集。 7、改善“外部编辑框_取密码框文本”当不是密码输入属性时不做任何处理,感谢易友【@wjt741106】反馈。 8、恢复“文本_加密”与...
第2章(\代码\第02章) ...• sample14.htm 提示用户...• sample11.htm 传递参数个数少于函数定义的参数个数(使用默认值) • sample12.htm Arguments对象的使用方法 • sample13.htm ...
如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。 15、error和exception有什么区别? error 表示恢复不是...
3 枚举类型 16. 4 静态导入 16. 5 可变长参数Varargs 16. 6 格式化输出 16. 7 使用ProcessBuilder执行本地命令 16. 8 泛型编程 16. 9 注释功能Annotation 16. 10 监控与管理虚拟机 ...
第三种:new 约束指定泛型类声明中的任何类型参数都必须有公共的无参数构造函数。 2.如何把一个array复制到arrayList里 foreach( object o in array )arrayList.Add(o); 3.datagrid.datasouse可以连接什么数据源 ...
3.5.3 通过引用来传递参数 3.5.4 方法的重载 3.6 封装 3.6.1 为什么要封装 3.6.2 类的设计 3.7 属性 3.7.1 语法 3.7.2 只读/只写属性 3.8 继承 3.8.1 继承的基本概念 3.8.2 创建派生类 3.8.3 对象的创建 3.8.4 使用...
3.4.2 传递命名参数 58 3.4.3 消除可选参数和命名参数的歧义 59 第3章快速参考 63 第4章 使用决策语句 65 4.1 声明布尔变量 65 4.2 使用布尔操作符 66 4.2.1 理解相等和关系操作符 66 4.2.2 理解条件逻辑操作...