`

客户端传递枚举类型参数

阅读更多

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

 

 

 

 

分享到:
评论

相关推荐

    Struts2中的参数传递

    1. **封装OGNL操作,实现值的传递**:Struts2通过对OGNL表达式的封装,实现从客户端传入的参数到Java对象之间的值传递。这一过程包括但不限于对各种复杂类型的处理,如数组、列表、映射等。 2. **类型转换**:为了...

    eSDK UC V100R005C10 接口参考 01(PC客户端, COM)

    这些数据类型通常包含了各种结构体、枚举值等,它们定义了接口参数和返回值的格式。例如,可能有表示用户信息的数据结构,或者代表通话状态的枚举值。理解这些数据类型对于正确传递和解析数据至关重要。 接口的输入...

    反射动态调用DLL方法,含参数

    反射是.NET框架提供的一种机制,它允许在运行时检查类型信息,包括类、接口、枚举、属性、方法等。通过System.Reflection命名空间中的类,如Assembly、Type、MethodInfo、ConstructorInfo等,我们可以动态地创建对象...

    c#整合zeroIce - 传递复杂对象

    ZeroIce支持传递各种数据类型,包括基本类型、数组、结构体、类和枚举。对于复杂的对象,你可以: 1. **定义数据类型**: 在`.ice`文件中定义复杂对象的结构,如类或结构体,包含所需的属性和成员。 2. **序列化与...

    onif.rar_java实例_java编程实例

    2. **HttpMethod**: HttpMethod是Spring框架中的一个枚举类型,它定义了常见的HTTP请求方法,如GET、POST、PUT、DELETE等。在进行HTTP请求操作时,开发人员可以使用这些常量来指定请求类型,以实现与HTTP服务器的...

    ZwQuerySystemInformation枚举进程线程的软件源码

    通过传递不同的参数,可以获取不同类型的系统信息。 ```vb Private Const SystemProcessAndThreadInformation As Long = 5 ``` 这里定义了一个常量 `SystemProcessAndThreadInformation`,其值为 5,代表请求的是...

    struts2枚举值、日期值转换示例

    在Struts2中,如果Action类的属性是枚举类型,系统默认可能无法正确地将字符串请求参数转化为枚举实例。为了解决这个问题,我们可以创建自定义的枚举值转换器。这个转换器需要实现`org.apache.struts2.conversion....

    Android-Kotlin开发的饭否客户端

    4. **高阶函数与Lambda表达式**:Kotlin支持函数作为参数传递,以及简洁的Lambda表达式,使得函数式编程变得简单。 5. **数据类和密封类**:数据类用于存储数据,自动生成equals(), hashCode() 和 toString() 方法...

    Elixir 的 Redis 命令.zip

    Redis 中的枚举类型只是可枚举的(通常),看一下xx | nx枚举iex&gt; Redis.set("key", "value", [:xx])["SET", [" ", "key"], [" ", "value"], [], [" ", "XX"]]iex&gt; Redis.set("key", "value", [:nx])["SET", [" ", ...

    工厂模式范例 详解枚举事件索引器委托有参委托属性方法等

    枚举在C#中的使用包括定义枚举类型、设置枚举成员的值、比较枚举值以及遍历枚举等操作。 事件(Event)是C#中的一种多线程通信机制,它允许对象向其他对象广播状态改变或其他重要消息。事件通常与委托(Delegate)...

    asp.net(C#)基础语法下载

    这定义了一个枚举类型`Gender`,其中包含三个成员:`Male`、`Female`和`Unknow`。 #### 5. 转换与类型检查 C#提供了多种方式来进行数据类型转换,包括`Convert`类的静态方法和类型强制转换(如`(int)i`)。此外,C#...

    ACCP4.0 S2 2006-9-17试题+答案

    题目中定义了一个名为`WeekDays`的枚举类型,并初始化一个`WeekDays`类型的变量`week`为2,然后输出该变量的值。 **知识点总结**: - 枚举类型是一种特殊的类,用于声明一组命名的常量。 - 可以通过显式赋值的方式...

    jsp源码实例2(获取表单参数).docx

    首先,设置响应的MIME类型为 `text/html`,然后获取一个 `PrintWriter` 对象,用于向客户端输出HTML内容。接下来,创建一个HTML表格,用于显示参数名和对应的值。 通过 `HttpServletRequest` 对象的 `...

    asp.net(C#)基础笔记大全

    - 如何使用可变参数 `params`,允许传入任意数量的同类型参数。 5. **类型转换:** - `Convert` 类提供的类型转换方法,如 `Convert.ToString()` 和 `Convert.ToInt32()`。 - 使用 `Cast` 或 `Convert` 进行类型...

    程序设计杂谈第三版

    - **枚举与实体类**:如果参数表示的是有限的选择集,使用枚举类型可以提高代码的可读性和安全性。而在某些情况下,传递实体类而不是其标识符(ID)可以更好地表达语义,减少查询操作,提高代码的可维护性和效率。 ...

    C#3.0语言规范[参考].pdf

    4. **避免泛型类中过多的类型参数**:过多的类型参数可能导致复杂性增加,难以理解和使用,应考虑重构或减少类型参数的数量。 5. **避免名字空间含有过少的类型**:合理组织命名空间,使其包含相关的一组类型,有利...

    java数据库连接CallableStatement

    对于 OUT 参数,需要先通过 `registerOutParameter` 方法进行注册,指定参数的位置、数据类型(使用 java.sql.Types 枚举)及可能的精度。然后在执行完存储过程后,可以通过 `getXXX` 系列方法获取 OUT 参数的值。 ...

    JavaScript基础学问点归纳(推举)_.docx

    JavaScript是一种广泛应用于网页和网络应用开发的脚本语言,它主要负责实现客户端的交互功能。以下是对给定文件中提及的JavaScript基础知识点的详细解释: 1. **全局变量与局部变量**: - 全局变量在整个程序范围...

    fxcop规则说明

    4. 避免在泛型类中使用过多的类型参数:过多的类型参数会使代码变得复杂,难以理解和使用,可能导致错误的类型组合。 5. 避免让名字空间含有过少的类型:合理的命名空间组织有助于代码的模块化和可读性,每个命名...

    JSP期末考试题2008.pdf

    11. `getParameterNames()`方法:该方法返回客户端传递的所有参数名,结果是一个枚举类型的实例。 12. `clearBuffer()`方法:`Out`对象的`clearBuffer()`方法用于清除缓冲区中的数据,并将数据写回客户端。 13. `...

Global site tag (gtag.js) - Google Analytics