`

客户端传递枚举类型参数

阅读更多

概述,在利用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();
    }
}

 

 

 

 

分享到:
评论

相关推荐

    基于TCP的服务器客户端程序设计.doc

    在 Java 1.5 版本中,Java 又引入了泛型编程(Generic Programming)、类型安全的枚举、不定长参数和自动装/拆箱等语言特性。 Java 不同于一般的编译执行计算机语言和解释执行计算机语言。它首先将源代码编译成二进 ...

    易语言 茶凉专用模块

    参数 传递参数, 整数型, 可空 .子程序 打开网页, 逻辑型, 公开, 打开指定网址(成功返回真,失败返回假) .参数 网址, 文本型, , 欲打开的网页地址 .子程序 弹出光驱, 逻辑型, 公开, 弹出光驱门。 mciSendString .子...

    Java核心技术II(第8版)

    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 ...

    jsp内置对象的用法

    config对象是在一个Servlet初始化时,JSP引擎向它传递信息用的,此信息包括Servlet初始化时所要用到的参数(通过属性名和属性值构成)以及服务器的有关信息(通过传递一个ServletContext对象) 序号 方 法 说 明 ...

    gsoap 2.8 (SOAP/XML 关于C/C++ 语言的自动化实现工具内附 CSharp webservice例子,及GSOAP client和server例子)

     SOAP服务的输入输出参数可以是简单的数据类型或复杂的数据结构,可以由WSDL解析器自动生成或手工定义。预编译器将自动生成序列化/反  序列化这些数据的代码,以便存根例程可以将这些数据以XML的方式编码或解码。 ...

    超级有影响力霸气的Java面试题大全文档

    如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。 18、error和exception有什么区别?  error 表示恢复...

    java jdk实列宝典 光盘源代码

    枚举类型; 静态导入; 格式化输出; 使用ProcessBuilder执行本地命令; 泛型编程; 监控和管理虚拟机;新的线程执行架构; 线程锁; 线程条件; 线程同步装置:semaphore countdownlatch cyclicbarrier exchanger; 17 java与...

    精易模块[源码] V5.15

    6、修正“文本_加密”返回文本传递给“文本_解密”后长度不正确BUG,改为返回字节集。 7、改善“外部编辑框_取密码框文本”当不是密码输入属性时不做任何处理,感谢易友【@wjt741106】反馈。 8、恢复“文本_加密”与...

    《javaScrip开发技术大全》源代码

    第2章(\代码\第02章) ...• sample14.htm 提示用户...• sample11.htm 传递参数个数少于函数定义的参数个数(使用默认值) • sample12.htm Arguments对象的使用方法 • sample13.htm ...

    java 面试题 总结

    如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。 15、error和exception有什么区别? error 表示恢复不是...

    Java JDK实例宝典

    3 枚举类型 16. 4 静态导入 16. 5 可变长参数Varargs 16. 6 格式化输出 16. 7 使用ProcessBuilder执行本地命令 16. 8 泛型编程 16. 9 注释功能Annotation 16. 10 监控与管理虚拟机 ...

    asp.net面试题

    第三种:new 约束指定泛型类声明中的任何类型参数都必须有公共的无参数构造函数。 2.如何把一个array复制到arrayList里 foreach( object o in array )arrayList.Add(o); 3.datagrid.datasouse可以连接什么数据源 ...

    ASP.NET3.5从入门到精通

    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 使用...

    Visual C#2010 从入门到精通(Visual.C#.2010.Step.By.Step).完整去密码锁定版 I部分

    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 理解条件逻辑操作...

Global site tag (gtag.js) - Google Analytics