在利用Spring进行Web后台开发时,经常会遇到枚举类型的绑定问题。一般情况下,如果Spring接收到的参数值为字符串类型,Spring会根据枚举的值与传入的字符串进行对应。假设有如下枚举
清单1:枚举定义
public enum Gender {
MALE, FEMALE;
}
http://localhost:8080/handle/enum?gender=MALE
但是,假如客户端传来的参数值不是枚举值对应的字符串,而是诸如整数值之类的值,Spring就没法做自动对应了。这种情况下该如何处理呢?
好了,从现在开始,我们将使用如下枚举进行参数绑定。
清单2:需要进行转换的枚举定义
package org.fhp.springbootdemo.entity;
import java.util.HashMap;
import java.util.Map;
public enum Gender implements BaseEnum {
MALE(1), FEMALE(2);
private int value;
private static Map valueMap = new HashMap<>();
static {
for(Gender gender : Gender.values()) {
valueMap.put(gender.value, gender);
}
}
Gender(int value) {
this.value = value;
}
@Override
public int getValue() {
return value;
}
public static Gender getByValue(int value) {
Gender result = valueMap.get(value);
if(result == null) {
throw new IllegalArgumentException("No element matches " + value);
}
return result;
}
}
清单3:枚举所需的实现接口
package org.fhp.springbootdemo.entity;
public interface BaseEnum {
int getValue();
}
好在Spring为我们提供了一个类型自动转换接口Converter,可以实现从一个Object转为另一个Object的功能。除了Converter接口之外,实现ConverterFactory接口和GenericConverter接口也可以实现我们自己的类型转换逻辑。
我们先来看一下Converter接口的定义:
清单4:Converter接口定义
public interface Converter {
T convert(S source);
}
下面给出一个字符串转换为Gender枚举的converter实现。需要注意的是,在Spring MVC和Spring Boot中,由于从客户端接收到的请求都被视为String类型,所以只能用String转枚举的converter。
清单5:String转Gender的Converter实现
package org.fhp.springbootdemo.converter;
import org.springframework.core.convert.converter.Converter;
public class StringToGenderConverter implements Converter {
@Override
public Gender convert(String source) {
return Gender.getByValue(Integer.parseInt(source));
}
}
public interface ConverterFactory {
Converter getConverter(Class targetType);
}
package org.fhp.springbootdemo.converter;
import org.fhp.springbootdemo.entity.BaseEnum;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
public class UniversalEnumConverterFactory implements ConverterFactory {
private static final Map converterMap = new WeakHashMap<>();
@Override
public Converter getConverter(Class targetType) {
Converter result = converterMap.get(targetType);
if(result == null) {
result = new IntegerStrToEnum(targetType);
converterMap.put(targetType, result);
}
return result;
}
class IntegerStrToEnum implements Converter {
private final Class enumType;
private Map enumMap = new HashMap<>();
public IntegerStrToEnum(Class enumType) {
this.enumType = enumType;
T[] enums = enumType.getEnumConstants();
for(T e : enums) {
enumMap.put(e.getValue() + "", e);
}
}
@Override
public T convert(String source) {
T result = enumMap.get(source);
if(result == null) {
throw new IllegalArgumentException("No element matches " + source);
}
return result;
}
}
}
package org.fhp.springbootdemo;
import org.fhp.springbootdemo.converter.UniversalEnumConverterFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class MyWebAppConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new UniversalEnumConverterFactory());
}
}
package org.fhp.springbootdemo.controller;
import org.fhp.springbootdemo.entity.Gender;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class HandleEnumController {
@RequestMapping("/handle/enum")
public Object handleEnum(@RequestParam("gender") Gender gender) {
Map dataMap = new HashMap<>();
dataMap.put("data", gender.name());
return dataMap;
}
}