3.1 创建Spring Boot项目
3.1.1 利用Spring Initializr创建项目
如2.3.1节所述,开发一个新的Spring Boot项目,首先要利用Spring Boot Initializr创建符合Spring Boot标准的项目,Initializr的工作原理与使用Maven Archetype创建项目是完全一致的,但Initializr对创建Spring Boot项目更友好。
首先,打开Spring Boot Initializr官网,将项目类型设定为Maven,项目语言设定为Java,此外还有以下参数需要指定:
• Spring Boot版本:2.2.10
• Group:com.broadview
• Artifact:coupon-cloud-center
• Name:broadview-coupon
• Package name:com.broadview.coupon
• Packaging:jar
• Java:8
然后单击GENERATE按钮,网站将产生一个空白的Spring Boot项目,并自动下载到本地,开发者可以将该项目导入自己熟悉的IDE,如IntelliJ。
此外,开发者也可以使用STS(Spring Tool Suit,Spring工具包)创建Spring Boot项目,STS的操作方式本书不会涉及,请读者自行探索。
3.1.2 项目结构
按照业务需求衡量优惠券项目,该项目复杂度相对较高,因此在设计系统时,我们将遵循DDD(Domain Drive Design,领域驱动设计)的最佳实践,先创建一个主项目spring-boot-starter-parent,用于定义子项目间的关系和整个项目需要使用的依赖,再按照不同的业务领域将整体项目分解为如下四个子项目:
• coupon-shared-components:提供工具类和POJO(Plain Ordinary Java Object,简单Java对象)的共享组件。
• coupon-calculation-service:提供优惠券计算服务。
• coupon-template-service:提供定制优惠券规则模板服务。
• coupon-user-service:提供用户领取和消费优惠券服务。
四个子项目各自负责自己的业务逻辑,并且它们的parent都是主项目。第一个子项目(coupon-shared-components)为其他三个子项目(coupon-calculation-service、coupon-template-service、coupon-user-service)提供工具类,除第一个子项目外,其他三个子项目均为Spring Boot项目。
此业务系统是依据某电商的真实优惠券业务相关场景抽象脱敏而来,其整体架构根据不同的用户场景和边界上下文(bounded context)将被分解为三个不同的微服务(即三个子项目coupon-calculation-service、coupon-template-service、coupon-user-service)。
此外,子项目coupon-shared-components为所有微服务(coupon-calculation-service、coupon-template-service、coupon-user-service)提供工具类,即该子项目被三个微服务所依赖。
从项目管理的角度评估系统,设计者倾向于将整体项目设计为一个父项目,其下有多个子项目。原因在于,当项目组采用敏捷开发时,可以将多个子项目并行开发。如果将所有功能都分配在一个大项目之中,那么并行开发的难度将会提高很多,而且对多团队协作开发也不友好。
按照上述设计,此项目应当将根目录作为父项目,其余四个项目作为该项目下的子项目,但项目实现却与此设计略有出入。这是因为在本书中,Spring Boot内容只是全书的一部分,为维护项目的扩展性,笔者将所有与Spring Boot相关的内容合并为一个模块,并将其根目录设定为父项目(如何扩展此项目?后续章节将会一一呈现)。
基于上述原因,项目的最终结构如图3-1所示。
图3-1 项目结构
3.1.3 在项目中添加Starter
为了开发的便捷性和可控的依赖管理,Spring Boot官方为广大开发者提供了各种各样的Starter(关于Starter请回顾2.3.1节)。在本节中,笔者将通过优惠券项目向读者展示Spring Boot Starter的各种功能。
在优惠券项目结构中,coupon-shared-components是一个公共模块,它为其他子模块提供工具类,我们无需为其添加任何Starter。coupon-calculation-service主要提供计算逻辑服务,我们需要通过添加spring-boot-starter-web依赖项引入Web支持,相关代码如下:
此外,该项目作为Spring Boot项目要成功运行,必须引入spring-boot-starter,但在项目中却没有显式地引入,为什么?要知道原因,可以利用Maven的dependency tree插件进行分析,相关分析日志如下:
分析上面的日志,可见Web Starter(spring-boot-starter-web)依赖于spring-boot-starter,calculation service直接依赖于Web Starter,从而间接地引入了spring-boot-starter依赖,因此无需重复显式地引入。
由于coupon template service需要访问数据库,因此它的依赖中必须引入两种Starter,相关代码如下:
同calculation service一样,我们不需要专门显式地引入spring-boot-starter。但由于它使用JPA访问数据库,所以需要加载对应的数据库驱动,本项目使用MySQL数据库,因此引入的是MySQL驱动,加载MySQL驱动的代码如下:
此外,application.yml文件中对应的数据库配置也不可缺少(参考Spring Boot配置文档),否则该项目将无法启动。
coupon user service也需要访问数据库并对外提供Web Service,因此它需要的Starter和template service的Starter是相同的。
三个微服务都要满足一些额外的非功能性需求,例如提供简单的监控和JMX功能等,根据2.3.4节Actuator的介绍,项目需要引入Actuator Starter,具体代码如下:
引入Actuator Starter之后,通过暴露各种不同的endpoint,Spring Boot项目可以提供以下功能:
• 监控服务的健康状况。
• 收集并展示各种所需的系统指标。
• 提供JMX console。
3.1.4 偷懒神器lombok
本项目除基于Spring Boot的服务外,还有一个共享组件,主要提供工具类和POJO,对于传统的POJO,与之相伴的是臃肿的Getter和Setter代码,而且使用者还要设计如何构造POJO对象,这些代码大多不可或缺但却非常烦琐。虽然很多IDE都提供生成工具,但这种代码风格完全不符合代码整洁之道,开发者需要更加优雅的解决方案。基于当前技术栈,当然非lombok莫属。
要使用lombok,需要为项目引入新依赖,具体代码如下:
lombok是一个在编译期处理代码注释(Annotation)的处理器,它提供了很多简单明了的注释供开发者使用,以此减少代码量。此外,它还通过注释提供了很多经典设计模式的最佳实践。
如何简化大量必需而又烦琐的代码,以一个简单的POJO为例,代码如下:
上述lombok代码的运行效果如下:
• Data注释:产生所有属性(field)的getter、setter、toString、equals、hashcode方法和一个构造函数。这个构造函数支持两种属性,一种是final修饰的属性,另一种是非final修饰但标有@NonNull的属性。
• NoArgsConstructor和AllArgsConstructor注释:顾名思义,就是各自产生一个无参数构造函数和一个全参数构造函数。此外,lombok还提供各种构造函数的注释。
• Builder注释:与Data不同,这是建造者(Builder)模式的一种实现方式。任何class只要添加了lombok的Builder注释,那么调用者在使用该class时,就可以按照建造者模式来构造此class的对象。
此外,lombok还提供了大量的代码最佳实践。比如Spring框架一般都推荐采用unchecked exception的方式来构建代码,但是在JDK中仍然存在大量的checked exception,解决此问题的较好方式是捕捉checked exception,再将其转化为runtime exception,从而无需逐层捕获异常。未使用lombok时的代码如下所示:
虽然可以达到捕获异常的目的,但是代码不够简洁、优雅。如果使用lombok的@SneakyThrows注解,则代码会简洁许多,示例如下:
事实上,lombok提供了非常多的功能来简化代码,笔者在本书中只演示了上述功能,更多的lombok功能请读者自己学习。
请注意,在引入lombok之后必须在IDE中安装相应的插件,否则将无法编译和运行含有lombok注释的代码。关于在IDE中安装lombok插件的过程,请读者自行研究。