The client passes the enumeration type parameter

In summary, when using Spring for web background development, we often encounter the binding problem of enumeration types. In general, if the parameter value received by Spring is a string corresponding to the enumeration value, Spring will correspond to the incoming string according to the enumeration value.

The enumeration class is as follows: 

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;
    }

    //Omit get/set methods. . .

    public static SexEnum getEnumByCode(String code) {
        return StringUtils.isBlank(code) ? null : enumMap.get(code);
    }
}

    Then, as long as the client sends the request, it can set the value of the parameter to BOY or GIRL. The request looks like this: 

   /**
     * Use /guest/getEnumByCode?sex=BOY, pass value method<br/>
     */
    @GetMapping("/guest/getEnumByCode")
    public String getEnumByCode(@RequestParam("sex") SexEnum sexEnum){
        return sexEnum.getMessage()+":"+sexEnum.getCode();
    }

    However, if the parameter value sent by the client is not the string corresponding to the enumeration value, but a value such as 01, 02 encoding, Spring cannot automatically correspond. How to deal with this situation? 

    At this time, we need to use org.springframework.core.convert.converter.Converter for data binding. Spring provides us with an automatic type conversion interface Converter<S, T>, which can realize the function of converting from one Object to another Object .

Let's first look at the definition of the Converter interface:

    Converter interface definition:

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);

}

   The first type represents the original type, the second type represents the target type, and then a convert method is defined in it, which takes the original type object as a parameter for conversion and returns the target type object. This interface can be implemented when we need to build our own converter.

  A converter implementation that converts a string to a SexEnum enumeration is given below. It should be noted that in Spring MVC and Spring Boot, since the requests received from the client are regarded as String types, only String can be used to convert enumerated converters.

    code show as below: 

@SpringBootConfiguration
public class CustomConverter implements Converter<String, SexEnum> {  
  
    @Override  
    public SexEnum convert(String code) {  
        return SexEnum.getEnumByCode(code);  
    }  
}    

   When the client sends a request, the value of the parameter can be set to 01 or 02. The request looks like this:

/**
     * Use /guest/getEnumByCode?sex=01, pass value method<br/>
     */
    @GetMapping("/guest/getEnumByCode")
    public String getEnumByCode(@RequestParam("sex") SexEnum sexEnum){
        return sexEnum.getMessage()+":"+sexEnum.getCode();
    }

   But if there are many enumeration classes in our project that need to be converted, too many Converter implementations are needed, which is not very good. What should we do?

   Fortunately, in addition to the Converter interface, spring also provides the ConverterFactory interface to implement type conversion logic.

   The ConverterFactory interface is as follows: 

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);

}
    The ConverterFactory interface defines a getConverter method that generates a Converter, and the parameter is the class of the target type. We can see that there are three generic types in ConverterFactory, S, R, T, where S represents the original type, R represents the target type, and T is a subclass of type R.

 

    It can be seen that the advantage of ConverterFactory compared to ConvertFactory is that ConverterFactory can convert the original type into a set of objects that implement the same interface type, while Converter can only be converted into one type. If we define other enumerations, then for each For an enumeration, we all need to implement a corresponding Converter, which is very inconvenient. With ConverterFactory, things become a lot easier. Now you can define a String to all ConverterFactory that implements the BaseEnum enumeration, and then generate the corresponding Converter according to the target type to perform the conversion operation.

   The use steps are as follows:

   step1: Define a public enumeration class interface BaseEnum, as follows:

/**
 * ClassName:BaseEnum <br/>
 * Function: public enumeration class interface<br/>
 * Date: December 25, 2017 2:31:52 PM <br/>
 * @author   [email protected]
 */
public interface BaseEnum {
    
    public String getCode();
}

    step2: The custom enumeration class implements the public enumeration class interface

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: Implement the ConverterFactory interface, as follows:

@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: The parameter value received by Spring is a string type, and Spring will correspond to the incoming string according to the value of the enumeration<br/>
     * date: December 25, 2017 3:59:15 PM <br/>
     *
     * @author [email protected]
     * @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: Add the implemented CustomConverterFactory to the springboot configuration, as follows:

@SpringBootConfiguration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(customConverterFactory());
    }
    
    /**
     * customConverterFactory: bind enumeration type parameters<br/>
     * @author [email protected]
     */
    @Bean
    public CustomConverterFactory customConverterFactory(){
        return new CustomConverterFactory();
    }
}   

    After completing the above 4 steps, when the client sends the request, the code of the enumeration class is passed as the value, for example, the code is 01 or 02, as follows   

@RestController
public class EnumController {
    /**
     * Use /guest/getEnumByCode?sex=01, pass value method<br/>
     */
    @GetMapping("/guest/getEnumByCode")
    public String getEnumByCode(@RequestParam("sex") SexEnum sexEnum){
        return sexEnum.getMessage()+":"+sexEnum.getCode();
    }
    /**
     * Use /guest/getEnumByCodeByPath/01, pass value method<br/>
     */
    @GetMapping("/guest/getEnumByCodeByPath/{sex}")
    public String getEnumByCodeByPath(@PathVariable("sex") SexEnum sexEnum){
        return sexEnum.getMessage()+":"+sexEnum.getCode();
    }
    
    /**
     * Use /guest/selectByObj?name=abc&age=20&sex=01, pass value method<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();
    }
}

 

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326410554&siteId=291194637