如何搭建一个能快速迭代业务,且稳定的Java 接口服务

459 阅读8分钟

         在Java的web开发世界中,spring boot 现在应该是应用最广泛的的了。spring boot要做一个接口服务的底子真的很快,使用maven 引入几个spring boot的starter接可以很快的可以开始写接口了。但是他是个基础框架,它不能保证你写的代码不出错或者少出错。那如何让写的代码更简洁,更少出错呢。这个文章我不想贴代码。因为我知道大家喜欢拿来主义。所以我最后会把脚手架的代码放出来。本篇文章只写我最近一年来不断对一个系统不断重构的实践。

1、代码即文档

      写一个接口对spring boot 来说真的很快。你只需要关心业务就好了。接收请求,返回数据spring都为你做好了。但是写出来的接口要给前端用吧,所以你需要一个强大的接口文档工具。开源的swagger2真的是一个很强大的接口文档生成工具。写代码的同时就把文档写出来了。真正的做到了代码即文档。

2、写可维护的接口

      什么样的接口是易于扩展的,什么样的接口是易维护的。我实践过程中发现,无论是什么方法,我们保证他是无参/单参是最好维护的。无参就不说了,没啥好解释的。为什么要单参数,很多人搞不清楚?相信大家都用AOP做过业务,也清楚AOP在接口的调用过程中拿到的参数是个数组,在代码运行期间,你起的那些参数名其实都是被擦除的,对jvm来说没有任何实际的意义。为什么要单参数是为了保证这种信息不丢失,或者丢失了无影响。可能这么解释还是一脸懵逼,我为什么要保证接口参数名的信息不丢失/丢失了无影响,那我来说一个具体使用场景你就明白这种设计的好处了。在电商服务中,为了保证一些访问频率高接口的响应,我们会对这些接口做数据做缓存。怎么做缓存,不是Java小白都知道用AOP加自定义注解来无侵入代码逻辑的方式来做缓存。假设现在我们在redis中用 PRORDUCT_DETAIL:{productId}来做key对商品的信息进行缓存。现在商品进行下架了或者上架了,你的上下架接口想必是一个,通过传一个productId和saleState来做这件事,如果你把这两个参数分开写了,你通用的AOP逻辑里就无法精准的对哪一个商品的缓存进行删除。因为你AOP的逻辑里不知道哪一个参数是productId。你就无法做到精准删除。如果你是单参数,你需要把productId,saleState封装成一个Dto类,这样在调用过程中你的'productId' 这个属性名就不会丢失,你就可以精准的去做自动的缓存失效。这只是一个小的应用场景,更复杂的场景中,多参数合并为对象来传参数,这样的接口如果有业务变动也能更好的扩展。你不必为要了业务需要多传一个参数的时候需要把所有调用的地方都改动一遍。而且有的上层接口并不一定能拿到你想要的结果,多参的接口会让你在业务改动的时候,牵一发而动全身。

3、降低自己的工作量,才能高效。

     在我们写代码的时候有很多无脑工作需要做,比如你想写一个新增商品的接口。几十个属性。要存到数据库的时候,你需要把传参数的dto转成po,如果你一个个的手写set(get)这不让人崩溃吗?效率奇差不说,而且容易漏啊。不如做个BaseEntity让所有的dto和po来继承的好,BaseEntity提供一个<T> convertTo(Class<T> clazz)来做。通过反射和Spring的BeanUtils来实现一个po的生成,啥你跟说反射的性能低?你们公司缺一个调用100w次多消耗1s的服务器资源吗?追求系统的性能不要矫正过枉。你的服务真的不缺那100w次多出1s的服务器消耗。我相信国内90%的公司的服务器大部分时间都是‘半睡半醒’的状态。真把业务能做到QPS缺这1s再说吧。

4、一个适合的ORM框架

     ORM框架对Java开发来说,最有名的莫过于以前是Mybatis/Hibernate . 现在Mybatis/Spring Data JPA。 从我在公司和我自己在家研究不断重构自己的代码来看。我更偏向于Spring Data JPA。说说我的感受吧!Mybatis框架是个好框架我不否认,但是它需要写sql的特性就代表了开发效率真的不高。然后他有一个插件Mybatis Plus 标准的增删改查都不需要写sql了。倒是解决了这种开发效率低问题。但是我依然强烈推荐JPA作为ORM。原因如下:

          (1)、在基础的增删改查上,用Mybatis Plus 和JPA应该差不多,但是JPA的查询返回是Optional能避免很多NullPointException的问题。如果你用的是JDK 11 (增加了很多Optional的方法),使用JPA返回的Optional让你的的代码优美,简洁,不容易出错。

         (2)、写法更简单,Mybatis Plus对属性查询需要构建QueryWapper对象,个人感觉很啰嗦。Spring Data的 findByXxx更简洁,多个属性的查询用Example来做更简洁。再复杂点用Query来做也很方便。

         (3)、业务扩张以后更容易迁移,比如开始用的是mysql做数据存储,最后随着数据扩大,需要把数据存储到Mongo/ES中使用Spring Data JPA能让你快速,更小成本的做迁移。

         (4)、mybatis的动态sql很强大,但是我觉得它不是一个团队协作好框架。太过自由的方式并不利于代码的质量。团队之中sql水平真的是良莠不齐的,项目大了以后很多sql的xml文件也很难维护。写的多错的多,这是亘古不变的道理,一个团队中能把Java代码写好的人,真的不见得sql能写好。

PS:一个完整的电商业务都可以不写一个句SQL 使用 Spring Data全家桶做掉了,我相信只要你熟悉了Spring Data 你会喜欢他的。所有的业务都可以通过设计来避免掉复杂sql 的,如果真复杂到必须要写巨大的sql来做业务,你真的该想想是不是选错了存储服务。

5、保护你的服务

      安全是一个API稳定的前提,如果今天被别人攻击导致服务宕机,那何谈服务稳定。 jwt+spring-security 是一个很好的套件来做这件事。而且我实践过程中发现接口分为三足够使用了。不要设计的太过细致,太过细致的设计除了提升系统的维护复杂程度,并没有什么卵用。第一类:不需要任何认证就可以访问的接口。第二类:需要C端认证的的接口。第三类:管理员才能访问的接口。用jwt+spring-security这很好实现。

6、监控你的服务

     服务快不快,稳不稳,用数据说话才最有说服力。OpenTracing的一个实现Jaeger在分布式应用中做链路追踪中非常棒。在单应用中也不是没有用武之地。用它来收集你接口的的调用时长,方便做优化还是很棒的。

7、保证接口统一的输出。异常做统一拦截。我建议每个接口的返回都应该用同一个ResponseVo。不要把你的异常抛给前端,你给他个500他也处理不了。在他眼里接口只有对错。对了给数据,错了给错误原因。不要把自己都解决不了的问题抛给别人。还有真的不要把所有的代码bug拦截成‘网络错误‘。怎么就网络错误。这种’甩锅‘的错误原因你除了给自己增加排查难度。还会让别人骂你。


屁话一大堆,该上代码了

github.com/jarven-he/s…

1、内置了swagger来做文档展示

2、jwt+spring-security来做安全认证。留好了认证的口子,留了个 todo 自己去接入你的账户系统吧

3、内部有一组API基本上是电商中商品的全部设计。当作demo来给你参照使用。

4、统一错误处理AOP没放在里面。参照自己的业务自行实现。

5、接口跨域的问题也已经处理好了

PS: 有问题欢迎关注 程序员的摸鱼之道 讨论