選用SpringCloud框架搭建微服務作業務後臺應用時,會涉及到大量的業務狀態值定義,通常常規作法是:前端
源於持久層存儲的優化規則,int類型要比varchar類型效率高不少,這套作法也是你們接受度很是高的。java
只是這裏有一個不是很方便的地方:狀態值映射的常量定義涉及前端和後臺兩部分,溝通的成本是一方面,另外若是狀態值有變化,須要兩組人員同時修改。node
在保證持久層的int類型存儲狀態值的前提下,主要是考慮業務狀態的可閱讀性問題和多處修改的問題,可閱讀性問題一部分能夠經過先後端人員定義常量來解決,但接口調試時仍是直接使用int類型,這部分的可閱讀性問題仍是存在,多處修改的問題須要重點解決。spring
本篇推薦的方案:數據庫
方案的優勢:後端
方案的缺點:瀏覽器
此實踐方案主要包含三部分:微信
先定義Enum國際化類,自定義Enum的序列化和反序列化類,並使用註解@JsonSerialize、@JsonDeserialize註冊到Spring的ObjectMapper中架構
@JsonDeserialize(using = DescEnumDeserializer.class) @JsonSerialize(using = DescEnumSerializer.class) public interface I18NEnum { /** * 獲取枚舉描述 * * @return */ String getDesc(); }
參考一下自定義的序列化實現:併發
/** * @author huangying */ public class DescEnumSerializer extends JsonSerializer<I18NEnum> { @Override public void serialize(I18NEnum value, JsonGenerator gen, SerializerProvider serializers) throws IOException { // 按類名+枚舉值名稱拼接配置文件key,所有大寫處理 String key = value.getClass().getSimpleName() + "." + StringUtils.upperCase(value.toString()); // I18NUtil爲國際化處理工具類 String data = I18NUtil.get(key, value.getDesc()); gen.writeString(data); } }
自定義的反序列化實現:
/** * @author huangying */ public class DescEnumDeserializer extends JsonDeserializer<I18NEnum> { @Override public I18NEnum deserialize(JsonParser p, DeserializationContext ctx) throws IOException { JsonNode node = p.getCodec().readTree(p); Class enumCls = BeanUtils.findPropertyType(p.currentName(), p.getCurrentValue().getClass()); List enumFields = EnumUtils.getEnumList(enumCls); String keyPrefix = enumCls.getSimpleName() + "."; for (Object enumField : enumFields) { I18NEnum i18NEnum = (I18NEnum) enumField; // I18NUtil爲國際化處理工具類 String data = I18NUtil.get(keyPrefix + StringUtils.upperCase(i18NEnum.toString()), i18NEnum.getDesc()); if (node.asText().equals(data)) { return i18NEnum; } } throw new I18NEnumException("enum:未知的枚舉類型"); } }
自定義一個專用異常,這樣看起來更加高大上:
/** * @author huangying */ public class I18NEnumException extends RuntimeException { public I18NEnumException(String message) { super(message); } }
這個國際化處理的工具類是通用的,讀取項目工程裏的messages.properties\messages_zh_CN.properties\messages_en.properties等配置文件的MessageSource信息,並根據具體的語言,返回信息來完成國際化顯示,代碼以下:
/** * @author huangying */ @Component public class I18NUtil { private static MessageSource messageSource; public I18NUtil(MessageSource messageSource) { I18NUtil.messageSource = messageSource; } public static String get(String key) { return messageSource.getMessage(key, null, LocaleContextHolder.getLocale()); } public static String get(String key, Object arg) { return messageSource.getMessage(key, new Object[]{arg}, LocaleContextHolder.getLocale()); } }
咱們舉一個enum定義的示例,有SUCCESS和FAIL兩個枚舉值,存儲在數據庫中的int值分別是1和2:
public enum OperateEnum implements I18NEnum { /** * 我的平常消費 */ SUCCESS(1, "SUCCESS"), /** * 裝修 */ FAIL(2,"FAIL"); private int index; private String desc; OperateEnum(int index, String desc) { this.index = index; this.desc = desc; } @Override public String getDesc() { return desc; } public int getIndex() { return index; } }
配置文件的寫法:
# messages.properties內容 # 枚舉類 OperateEnum.SUCCESS=success OperateEnum.FAIL=fail # messages_zh_CN.properties內容 # 枚舉類 OperateEnum.SUCCESS=操做成功 OperateEnum.FAIL=操做失敗
在SpringCloud環境下,添加對國際化語言的處理,咱們統一將國際語言標識放在request header的lang裏面:
/** * @author huangying */ public class I18NLocalResolver implements LocaleResolver { @Override public Locale resolveLocale(HttpServletRequest request) { String lang = request.getHeader("lang"); //獲取jvm默認locale Locale locale = Locale.getDefault(); if (lang != null) { locale = new Locale(lang); } return locale; } @Override public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { } }
在接口裏只須要將enum類返回,在@ResponseBody進行處理時便可觸發enum國際化的序列化方法,示例接口以下:
@ApiOperation(value = "枚舉值國際化示例") @ApiImplicitParams({ @ApiImplicitParam(name = "uid", value = "操做人員ID", paramType = "header", dataType = "Long")}) @RequestMapping(value = "/test/enums", method = RequestMethod.GET) public Result get( @RequestHeader(value = "lang") String lang) { return Result.success(EnumUtils.getEnumList(OperateEnum.class)); }
MappingJackson2HttpMessageConverter轉換器默認將@RequestBody的內容作反序列化處理,若是enum的國際化值傳遞給了客戶端,若須要正確處理客戶端提交的枚舉值國際化內容,最簡單的辦法是將enum定義在@RequestBody的對象中,就能自動觸發enum的自定義反序列化方法,並獲得指望的結果。
若在@RequestParam修飾的參數上定義enum對象,請求中的String轉換成enum是經過org.springframework.core.convert.support.StringToEnumConverterFactory 來實現的,該類實現了接口 ConverterFactory ,經過調用 Enum.valueOf(Class, String) 實現了這個功能,而不會觸發enum枚舉值的反序列化。所以只能處理與枚舉值相同的字面值(name),enum枚舉值國際化處理後,可能與字面值不相同,直接使用@RequestParam來轉換,會報錯。
若是要讓@RequestParam可以觸發enum枚舉值的反序列化操做,能夠嘗試重寫springmvc的參數轉換器,此處略。
enum枚舉值的國際化處理,是個很是有意思的改進,既可能解決閱讀性的問題,又提升了業務定義的內聚性,此方案的應用取決於先後端的編碼習慣,若是是在項目初期,先後端童鞋溝通確認後能夠嘗試此方案,但願對你有幫助。
專一Java高併發、分佈式架構,更多技術乾貨分享與心得,請關注公衆號:Java架構社區
能夠掃左邊二維碼添加好友,邀請你加入Java架構社區微信羣共同探討技術
![Java架構社區](https://s4.51cto.com/images/blog/202003/22/23e2ffe325b89476368d208448a60b9a.jpg?x-oss-
process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=)