一、概述
在利用Spring进行Web后台开发时,经常会遇到枚举类型的绑定问题。一般情况下,如果Spring接收到的参数值为字符串类型,Spring会根据枚举的值与传入的字符串进行对应。假设有如下枚举
清单1:枚举定义
public enum Season {
Spring("春"),Summer("夏"),Autumn("秋"),Winter("冬");
}
那么,只要客户端在发送请求时,将参数的值设为MALE或FEMALE即可。请求类似如下形式:
http://localhost:8080/handle/enum?season=Summer
但是,假如客户端传来的参数值不是枚举值对应的字符串,而是诸如整数值之类的值,Spring就没法做自动对应了。这种情况下该如何处理呢?
二、枚举与接口定义
好了,从现在开始,我们将使用如下枚举进行参数绑定。
清单2:需要进行转换的枚举定义
public enum Season implements IBaseEnum{
Spring("春"),Summer("夏"),Autumn("秋"),Winter("冬");
private static final Map seaMap = new HashMap();
static {
for (Season season : Season.values()) {
seaMap.put(season.value, season);
}
}
private String value;
private Season(String value) {
this.value = value;
}
@Override
public String getValue() {
return this.value;
}
/**
* 根据值获取枚举
* @param value
* @return
*/
public static Season getEnumByValue(String value) {
return seaMap.get(value);
}
}
在这里,我们令所有的枚举都实现IBaseEnum接口,以便转换时使用。IBaseEnum接口定义如下:
清单3:枚举所需的实现接口
public interface IBaseEnum {
/**
* 获取标示
* @return
*/
String getValue();
}
三、ConverterFactory接口
ConverterFactory的出现可以让我们统一管理一些相关联的Converter。顾名思义,ConverterFactory就是产生Converter的一个工厂,确实ConverterFactory就是用来产生Converter的。我们先来看一下ConverterFactory接口的定义:
清单4:ConverterFactory的接口定义
public interface ConverterFactory {
Converter getConverter(Class targetType);
}
我们可以看到ConverterFactory接口里面就定义了一个产生Converter的getConverter方法,参数是目标类型的class。我们可以看到ConverterFactory中一共用到了三个泛型,S、R、T,其中S表示原类型,R表示目标类型,T是类型R的一个子类。
可以看出,ConverterFactory相比ConvertFactory的好处在于,ConverterFactory可以将原类型转换成一组实现了相同接口类型的对象,而Converter则只能转换成一种类型。这样做的坏处在于,假如我们又定义了其他枚举,那么对于每一个枚举,我们都需要实现一个对应的Converter,十分的不方便。而有了ConverterFactory之后,事情就变得简单了不少。现在可以定义一个String到所有实现了IBaseEnum的枚举的ConverterFactory,然后根据目标类型生成对应的Converter来进行转换操作。如清单5所示。
@SuppressWarnings("all")
public class BaseEnumFactory implements ConverterFactory{
/**
* 缓存IBaseEnum实现类
*/
private static final Map, Converter> cacheMap = new HashMap<>();
@Override
public Converter getConverter(Class targetType) {
Converter converter = cacheMap.get(targetType);
if (converter == null) {
converter = new BaseEnumImpl(targetType);
cacheMap.put(targetType, converter);
}
return (Converter) converter;
}
/**
* 内部转换类
* @author zhaoyong
*
* @param
*/
private class BaseEnumImpl implements Converter {
private Class clazz;
private Map enumMap = new HashMap<>();
private BaseEnumImpl(Class clazz) {
this.clazz = clazz;
T[] enums = clazz.getEnumConstants();
for (T t : enums) {
enumMap.put(t.getValue(), t);
}
}
@Override
public T convert(String source) {
return enumMap.get(source);
}
}
}
四、集成至Spring Boot
在Spring Boot中,可以通过覆盖addFormatter方法来实现对Converter和ConverterFactory的绑定。
清单6:在配置中绑定ConverterFactory
@Configuration
public class MyWebAppConfigurer implements WebMvcConfigurer{
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new BaseEnumFactory());
WebMvcConfigurer.super.addFormatters(registry);
}
}
当然,也可以通过registry.addConverter()方法来绑定Converter。
在Controller中,采用如下方式来进行接收,和平常接收参数是一样的用法。
清单7:在Controller中的用法
@RestController
public class HandleEnumController {
@RequestMapping("/handle/enum")
public Object handleEnum(@RequestParam("season") Season season) {
return season;
}
}
参考:https://blog.csdn.net/u014527058/article/details/62883573