本人负责一个项目上线了,非常开心。这个项目是To C的,未来应用的访问压力较大,并且要求高可用性,提供7*24小时的不间断服务。根据项目的业务需求以及团队情况,采用Spring Cloud微服务架构。把用到的技术,和解决的技术问题,做一个总结。
在Java应用程序的开发中,枚举是经常被用到的功能,常常被用来约束几个特定的值,例如状态、类型、性别等。在我们的应用中,也大量应用了枚举,并且做了一些的改进,以期能够更优雅的使用枚举。
在Java中,枚举是用enum关键字来定义的。对其使用的过程中,做了更强的约束,定义了所有枚举必须实现的接口,BaseEnum,要求必须满足有value和desc两个值。value表示真实的值,desc表示其描述。
![53fc7544cad5721677b4efc0ce52d53a.png](https://img-blog.csdnimg.cn/img_convert/53fc7544cad5721677b4efc0ce52d53a.png)
用法非常简单,实现BaseEnum接口,并指定value和desc的类型就可以了。以性别的枚举为例:
![355ebc9364bcce2b5f6e2f5eab160039.png](https://img-blog.csdnimg.cn/img_convert/355ebc9364bcce2b5f6e2f5eab160039.png)
优雅的使用枚举,需要解决四个问题:
一、枚举型数据的存储和读取,能够把value存储到表中的字段,并且在读取数据的时候,能够正确映射到实体类(entity)的属性。
二、枚举型数据的能够在前端被简单的使用。数据要通过json的方式输出到前端,前端如何才能更简单有效的使用枚举,至关重要。
三、枚举型数据的接收。前端或者其他调用方,通过枚举值调用服务,服务能够正确的接收到枚举。
四、枚举型数据的缓存,能够正确的从redis中读写。能够解决上述4个问题,就能保证枚举在服务内部、服务之间以及前后端之间正确传递,就像使用Integer、String一样简单的使用枚举值。
一、枚举型数据的存储和读取。
数据库的读写是采用MyBatis框架实现的,并且应用了一个插件,是baomidou。这个插件确实非常值得推荐,对entity的CURD提供了默认支持。更重要的是,也提供了对枚举的支持,只需要实现IEnum接口就可以了。getValue的值,就是存储到数据库字段的值。
![2f76720569474bb90489b58161b8ba45.png](https://img-blog.csdnimg.cn/img_convert/2f76720569474bb90489b58161b8ba45.png)
再来看一下我定义的BaseEnum接口,是继承了IEnum接口的,也就是说,实现BaseEnum接口的枚举,都默认实现了IEnum接口。
如果没有用到baomidou插件怎么办?MyBatis提供了对类型的扩充机制,下一节《优雅的使用字典》中详细描述。
二、枚举值的前端应用。前端对枚举值的应用,无外乎展示和编辑/选取两种场景。
展示场景非常简单,在页面上正确展示描述(desc值)就可以了。比较一般的做法就是,给前端返回value值,前端根据value值替换desc进行展示。这样做前端工作量有点儿大,如果后端的枚举值有变动,前端的代码需要同步进行调整。我的解决方案就是,在输出给前端数据的时候,把value和desc两个字段同时写在json文档中。前端的同学拿到数据直接使用即可,避免了转换的过程。
![810ca8828fd4dd4843163dcb0e755a16.png](https://img-blog.csdnimg.cn/img_convert/810ca8828fd4dd4843163dcb0e755a16.png)
输出结果如上所示,性别(gender)字段在json中变成了两个字段,约定好xxTitle就是这个字段的描述(desc)值。
是怎么做到的呢?这就要感谢jackson了。jackson提供了对序列化的扩展能力,可以实现自定义的序列化。并且在配置中,使用jackson作为默认的消息转换器。看到这里,应该算是打开了一扇大门,通过这种方法,可以自定义特定类型数据的实例化。我把关键代码粘贴出来。
![63725c3e6236903ca91a39b240cdb7f5.png](https://img-blog.csdnimg.cn/img_convert/63725c3e6236903ca91a39b240cdb7f5.png)
![f91b392d46c53f6468640233d4178fc9.png](https://img-blog.csdnimg.cn/img_convert/f91b392d46c53f6468640233d4178fc9.png)
再来看一下编辑/选取的场景。我截图了微信公众号的后台,看下状态字段,有3种状态,这里就可以用枚举来实现。最直接的方法,就是前后端的工程师同时定义枚举,并约定好取值,看似非常简单。随着枚举值的增多,前后端需要保持一致,任何变动都要事先沟通,后端工程师做了修改,前端工程师必须同步修改。事情就这样变得越来越烦琐复杂。
![e33521b73e9cc6d3af3039ccfbaa25df.png](https://img-blog.csdnimg.cn/img_convert/e33521b73e9cc6d3af3039ccfbaa25df.png)
有没有简单容易一些的方法呢?我的解决方案是给前端提供一个接口,输出所有的枚举定义,前端只需根据枚举的编码,选用适当的枚举就可以了。看下效果:
![cd5bc3c7d3e020cda25575119d20a64b.png](https://img-blog.csdnimg.cn/img_convert/cd5bc3c7d3e020cda25575119d20a64b.png)
怎么实现的呢?这里的实现逻辑类似于MapperScan,即定义好扫描的package,在这个package中的所有枚举,组成一个list,然后定义controller,输出这个list就可以了。具体的代码逻辑有些复杂,这里就不展开了。
三、枚举型数据的接收。在java程序接收参数的时候,对于Post提交的json字符串,是采用了jackson提供的逆序列化能力。并且呢,jackson默认就提供了对枚举的支持,并不需要自定义逆序列化方式。具体就是@JsonCreator注解。
![64300ede8c0744dc79bf2aaf0758f059.png](https://img-blog.csdnimg.cn/img_convert/64300ede8c0744dc79bf2aaf0758f059.png)
对于Get提交的数据,或者post form提交的数据,该如何处理呢?我的方案是,自定义自己的@FormParam注解,此注解可以把get参数、post form参数以及json字符串,组装在一起,通过jackson转换成特定的VO。这样就完美解决了传入参数中枚举值的转换。
四、枚举型数据的缓存。数据的缓存,是存储到redis的。通过上面的介绍,已经知道jackson对枚举的序列化、逆序列化都有了很好的支持。缓存也是一样的道理,只要解决这两个过程就好了。存储和读取数据的时候,统一约定好用如下定义的ObjectMapper就可以了。
![64c1d7f90517e4263919fb786785019e.png](https://img-blog.csdnimg.cn/img_convert/64c1d7f90517e4263919fb786785019e.png)
通过上面的介绍,在整个应用过程中,枚举类型的数据就像Integer、String等一样,被优雅的使用。在这个过程中,解决问题的核心出发点,是序列化和逆序列化。我这里采用了jackson作为json的序列化工具包,如果采用其他工具包,也应有类似的解决方案。
后续的内容可能有:
- 优雅的使用字典
- 自实现的接口代理
- 读写分离
- 分布式ID
- 使用缓存
- 使用队列
- 使用retry
- eureka安全模式
- 。。。
![e9237d271817571a83762ae4b7301834.png](https://img-blog.csdnimg.cn/img_convert/e9237d271817571a83762ae4b7301834.png)