选用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.propertiesmessages_zh_CN.propertiesmessages_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架构社区微信群共同探讨技术