1. 程式人生 > >springboot mybatis自定義枚舉enum轉換

springboot mybatis自定義枚舉enum轉換

ger str string類型 配置 tex except package figure type

原文鏈接:https://blog.csdn.net/u014527058/article/details/62883573

一、概述

在利用Spring進行Web後臺開發時,經常會遇到枚舉類型的綁定問題。一般情況下,如果Spring接收到的參數值為字符串類型,Spring會根據枚舉的值與傳入的字符串進行對應。假設有如下枚舉

清單1:枚舉定義

public enum Gender {
MALE, FEMALE;
}

那麽,只要客戶端在發送請求時,將參數的值設為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<Integer, Gender> 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; } }

在這裏,我們令所有的枚舉都實現BaseEnum接口,以便轉換時使用。BaseEnum接口定義如下:
清單3:枚舉所需的實現接口

package org.fhp.springbootdemo.entity;

public interface BaseEnum {
int getValue();
}

三、Converter接口

好在Spring為我們提供了一個類型自動轉換接口Converter<S, T>,可以實現從一個Object轉為另一個Object的功能。除了Converter接口之外,實現ConverterFactory接口和GenericConverter接口也可以實現我們自己的類型轉換邏輯。

我們先來看一下Converter接口的定義:

清單4:Converter接口定義

public interface Converter<S, T> { 

T convert(S source); 

}

我們可以看到這個接口是使用了泛型的,第一個類型表示原類型,第二個類型表示目標類型,然後裏面定義了一個convert方法,將原類型對象作為參數傳入進行轉換之後返回目標類型對象。當我們需要建立自己的converter的時候就可以實現該接口。
下面給出一個字符串轉換為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<String, Gender> {

    @Override
    public Gender convert(String source) {
        return Gender.getByValue(Integer.parseInt(source));
    }
}

四、ConverterFactory接口

ConverterFactory的出現可以讓我們統一管理一些相關聯的Converter。顧名思義,ConverterFactory就是產生Converter的一個工廠,確實ConverterFactory就是用來產生Converter的。我們先來看一下ConverterFactory接口的定義:

清單6:ConverterFactory的接口定義

public interface ConverterFactory<S, R> { 

<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來進行轉換操作。如清單7所示。有了ConverterFactory,就可以取代清單5中的Converter了。

清單7:ConverterFactory轉換

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<String, BaseEnum> {

private static final Map<Class, Converter> converterMap = new WeakHashMap<>();

@Override
public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
Converter result = converterMap.get(targetType);
if(result == null) {
result = new IntegerStrToEnum<T>(targetType);
converterMap.put(targetType, result);
}
return result;
}

class IntegerStrToEnum<T extends BaseEnum> implements Converter<String, T> {
private final Class<T> enumType;
private Map<String, T> enumMap = new HashMap<>();

public IntegerStrToEnum(Class<T> 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;
}
}
}

五、集成至Spring Boot

在Spring Boot中,可以通過覆蓋addFormatter方法來實現對Converter和ConverterFactory的綁定。

清單8:在配置中綁定ConverterFactory

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

當然,也可以通過registry.addConverter()方法來綁定Converter。
在Controller中,采用如下方式來進行接收,和平常接收參數是一樣的用法。

清單9:在Controller中的用法

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<String, Object> dataMap = new HashMap<>();
dataMap.put("data", gender.name());
return dataMap;
}
}

springboot mybatis自定義枚舉enum轉換