一年前整顿的 SpringBoot 我的项目标准怕丢了,毕竟过后用心写了,当初补传到博客上。

1. 利用划分标准

利用零碎开发时,后端服务须要思考依照模块/微服务的划分准则来划分出多个SpringBoot模块,以便将来微服务化的重构。利用划分标准/准则如下:

  • 横向拆分:依照不同的业务域进行拆分,例如订单、营销、风控、积分资源等。造成独立的业务畛域微服务集群。
  • 纵向拆分:把一个业务性能里的不同模块或者组件进行拆分。例如把公共组件拆分成独立的原子服务,下沉到底层,造成绝对独立的原子服务层。这样一纵一横,就能够实现业务的服务化拆分。

要做好微服务的分层,须要梳理和抽取外围服务、公共利用,作为独立的服务下沉到外围和公共能力层,逐步造成稳固的服务中心,使前端利用能更疾速的响应多变的市场需求。服务拆分是越小越好吗?微服务的大与小是绝对的。比方在初期,咱们把交易拆分为一个微服务,然而随着业务量的增大,可能一个交易系统曾经缓缓变得很大,并且并发流量也不小,为了撑持更多的交易量,会把交易系统,拆分为订单服务、招标服务、转让服务等。因而服务的拆分力度需与具体业务相结合,总的准则是服务外部高内聚,服务之间低耦合。

2. 我的项目创立标准

一个利用零碎中会依据状况,至多会创立一个SpringBoot我的项目及多个SpringBoot模块,创立工程时听从以下规定:

  • GroupID 格局:com.{公司/BU }.业务线 [.子业务线]。

    阐明:{公司/BU} 例如:alibaba/taobao/tmall/aliexpress 等 BU 一级;子业务线可选。

    正例:com.taobao.jstorm 或 com.alibaba.dubbo.register

  • ArtifactID 格局:产品线名-模块名。语义不反复不脱漏,先到地方仓库去查证一下。

    正例:dubbo-client / fastjson-api / jstorm-tool
  • Version 格局

    二方库版本号命名形式:主版本号.次版本号.订正号

    1) 主版本号:产品方向扭转,或者大规模 API 不兼容,或者架构不兼容降级。

    2) 次版本号:放弃绝对兼容性,减少次要性能个性,影响范畴极小的 API 不兼容批改。

    3) 订正号:放弃齐全兼容性,修复 BUG、新增主要性能个性等。

    阐明:留神起始版本号必须为:1.0.0,而不是 0.0.1。

2.1. 父模块创立标准

一个利用零碎中会创立一个或多个SpringBoot我的项目,每个Spring Boot我的项目创立过程中(New -> Project),遵循如下标准:

名称标准
我的项目类型Spring Initializr
Group遵循java包名标准,到公司层
Artifact<我的项目/业务核心简称>-service 等
TypeMaven Project
LanguageJava
PackagingPOM
Java Version放弃默认
Name项目名称,与Artifact雷同
Package<Group>.<我的项目/业务核心简称>
Version可选取以后工夫较新的稳定版,例如release版本
其中 Version 版本号命名形式: 主版本号.次版本号.订正号
  1. 主版本号:产品方向扭转,或者大规模 API 不兼容,或者架构不兼容降级。
  2. 次版本号:放弃绝对兼容性,减少次要性能个性,影响范畴极小的 API 不兼容批改。
  3. 订正号:放弃齐全兼容性,修复 BUG、新增主要性能个性等。

留神:起始版本号必须为:1.0.0,而不是 0.0.1。

3. POM标准

3.1. 父模块POM标准

父模块(tl-service)POM文件参考:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.4.5</version>        <relativePath/>    </parent>    <groupId>com.df.tl</groupId>    <artifactId>tl-service</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>tl-service</name>    <description>tl我的项目的父模块</description>    <!--    属性-->    <properties>        <java.version>1.8</java.version>        <spring-parent.version>2.4.5</spring-parent.version>        <lombok.version>1.18.0</lombok.version>        <mybatis.version>1.3.2</mybatis.version>        <mysql.version>8.0.16</mysql.version>        <druid.version>1.1.13</druid.version>    </properties>    <!--    子模块,申明-->    <modules>        <module>tl-api</module>        <module>tl-project-service</module>        <module>tl-task-service</module>    </modules>    <!--    打包形式 pom-->    <packaging>pom</packaging>    <!--    dependencyManagement 治理-->    <dependencyManagement>        <dependencies>            <!--            spring web-->            <dependency>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-starter-web</artifactId>                <version>${spring-parent.version}</version>            </dependency>            <!--            spring test-->            <dependency>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-starter-test</artifactId>                <scope>test</scope>                <version>${spring-parent.version}</version>            </dependency>            <!--            lombok-->            <dependency>                <groupId>org.projectlombok</groupId>                <artifactId>lombok</artifactId>                <version>${lombok.version}</version>            </dependency>            <!--            mybatis-->            <dependency>                <groupId>org.mybatis.spring.boot</groupId>                <artifactId>mybatis-spring-boot-starter</artifactId>                <version>${mybatis.version}</version>            </dependency>            <!--            mysql-->            <dependency>                <groupId>mysql</groupId>                <artifactId>mysql-connector-java</artifactId>                <version>${mysql.version}</version>            </dependency>            <!--            druid 数据源-->            <dependency>                <groupId>com.alibaba</groupId>                <artifactId>druid-spring-boot-starter</artifactId>                <version>${druid.version}</version>            </dependency>        </dependencies>    </dependencyManagement>    <build>        <!--        pluginManagement 治理-->        <pluginManagement>            <plugins>                <plugin>                    <groupId>org.springframework.boot</groupId>                    <artifactId>spring-boot-maven-plugin</artifactId>                </plugin>            </plugins>        </pluginManagement>    </build></project>

须要留神的中央如下:

  • properties:除了java.version以外,能够自定义属性(非强制),如maven依赖的版本号。
  • modules:须要申明所有子模块。
  • packaging:父模块应用 pom 。该标签有pom/jar/war三种属性,但父模块的性能通常只是打包,故应用 pom 即可。
  • dependencyManagement:父模块倡议应用 dependencyManagement 治理依赖,相较于 dependencies 来说更灵便,不会导致子模块引入不必要的依赖。可自行查问材料,比照 dependencyManagementdependencies 的区别。
  • pluginManagement:父模块倡议应用 pluginManagement 治理插件,相较于 plugins 来说更灵便。可自行查问材料,比照 pluginManagementplugins 的区别。

3.2. 子模块POM标准

子模块(tl-project-service)POM文件参考:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>com.df.tl</groupId>        <artifactId>tl-service</artifactId>        <version>0.0.1-SNAPSHOT</version>    </parent>    <groupId>com.df.tl</groupId>    <artifactId>tl-project-service</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>tl-project-service</name>    <description>项目管理模块</description>    <!--    打包形式 jar-->    <packaging>jar</packaging>    <dependencies>        <!--        spring web-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <!--        spring test-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>        </dependency>        <!--            lombok-->        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>

须要留神的中央如下:

  • parent:继承父模块。
  • packaging:子模块应用jar
  • dependencies:无论父模块用dependencyManagement 还是 dependencies 形式,子模块都一律用 dependencies。如果是前者,须要申明反复依赖,但可省略版本号;如果是后者,无需反复依赖。
  • plugins:无论父模块用pluginManagement 还是 plugins 形式,子模块都一律用 plugins。如果是前者,须要申明反复依赖,但可省略版本号和配置的细节;如果是后者,无需反复依赖。

4. 分层标准

  • 凋谢接口层:可间接封装 Service 办法裸露成 RPC 接口;通过 Web 封装成 http 接口;网关管制层等。
  • 终端显示层:各个端的模板渲染并执行显示的层。以后次要是 velocity 渲染,JS 渲染,JSP 渲染,挪动端展现等。
  • Web 层:次要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简略解决等。
  • Service 层:绝对具体的业务逻辑服务层。
  • Manager 层:通用业务解决层,它有如下特色:
    1) 对第三方平台封装的层,预处理返回后果及转化异样信息。

    2) 对 Service 层通用能力的下沉,如缓存计划、中间件通用解决。

    3) 与 DAO 层交互,对多个 DAO 的组合复用。

  • DAO 层:数据拜访层,与底层 MySQL、Oracle、Hbase、OB 等进行数据交互。
  • 内部接口或第三方平台:包含其它部门 RPC 凋谢接口,根底平台,其它公司的 HTTP 接口。

这里同样提供单个我的项目工程的构造分层示例:

.├── java│   └── com│       └── df│           └── learn│               └── exampleapi│                   ├── ExampleApiApplication.java│                   ├── config│                   │   └── ThreadPoolConfig.java│                   ├── constant│                   │   └── ThreadConstant.java│                   ├── controller│                   │   └── DemoController.java│                   ├── manager│                   │   └── UserManager.java│                   ├── mapper│                   │   └── HrUserMapper.java│                   ├── pojo│                   │   ├── bo│                   │   │   └── EmailInfoBO.java│                   │   ├── dto│                   │   │   └── UserInfoDTO.java│                   │   └── po│                   │       └── HrUserPO.java│                   ├── service│                   │   ├── DemoService.java│                   │   └── impl│                   │       └── DemoServiceImpl.java│                   └── util│                       └── EmailUtil.java└── resources    ├── application-dev.yml    ├── application-prod.yml    ├── application-uat.yml    ├── application.yml    ├── ehcache.xml    ├── logback-spring.xml    ├── mapper    │   └── HrUserMapper.xml    ├── static    └── templates

当初解释一下这些工程构造:

  • controller:前端管制层 Controller。
  • service:数据服务接口层 Service。
  • manager:通用业务解决层 Manager。
  • service.impl:数据服务接口实现层 Service Implements。
  • config:配置类目录。
  • consts:常量类目录。
  • util:工具类目录。
  • mapper(dao):dao层目录,如果是MyBatis我的项目能够用mapper。
  • pojo:蕴含PO/BO/VO/DTO等目录。
POJO分层
  • POJO(Plain Ordinary Java Object):是 DO/DTO/BO/VO 的统称,POJO专指只有setter/getter/toString的简略类,包含DO/DTO/BO/VO等,但禁止命名成 xxxPOJO。
  • PO( Persistant Object):与数据库表构造一一对应。也有应用 DO( Data Object)代替的。
  • DTO( Data Transfer Object):数据传输对象,Service或Manager向外传输的对象,即也是Controller中,Request或Response所封装的对象。
  • BO( Business Object):业务对象。能够了解成Java开发过程中,形象进去的一些与表构造无关的POJO,可能蕴含一到多个DO。
  • VO( View Object):展现对象,它的作用是把某个指定页面(或组件)的所有数据封装起来。VO不常见,因为和DTO太类似,根本都用DTO代替。例如:男/女在数据库中存储为0/1,DTO中显示的是0/1,VO中应该显示的是男/女。但没必要纠结,集体偏好间接用DTO。

5. 多环境配置标准

多环境下的YAML配置文件,标准如下:

  • application.yml -- 主配置文件
  • application-dev.yml -- 开发环境配置文件
  • application-uat.yml -- 测试环境配置文件
  • application-prod.yml -- 正式环境配置文件

配置文件示例如下:

spring:  profiles:    active: prod  datasource:    driver-class-name: com.mysql.jdbc.Driver    url: jdbc:mysql://127.0.0.1:3306/test    username: root    password: root

一个我的项目上个别有好多套环境,开发环境,测试环境,UAT环境,生产环境,每个环境的参数不同,所以咱们就须要把每个环境的参数配置到对应的yml文件中,能够通过在主配置文件中启用以后的配置文件,例如:

spring:  profiles:    active: prod

这行配置在application.yml 文件中,意思是以后失效的配置文件是application-prod.yml。

6. 异样解决标准

各层的异样解决要求
  • DAO层:产生的异样类型有很多,无奈用细粒度的异样进行 catch,应用 catch(Exception e)形式,并 throw new DAOException(e),不须要打印日志,因为日志在 Manager/Service 层肯定须要捕捉并打印到日志文件中去,如果同台服务器再打日志, 节约性能和存储。
  • Service层:在 Service 层出现异常时,必须记录出错日志到磁盘,尽可能带上参数信息, 相当于爱护案发现场。
  • Manager层:Manager 层与 Service 同机部署,日志形式与 DAO 层解决统一,如果是独自部署,则采纳与 Service 统一的解决形式。
  • Web层:Web 层绝不应该持续往上抛异样,因为曾经处于顶层,如果意识到这个异样将导致页面无奈失常渲染,那么就应该间接跳转到敌对谬误页面, 尽量加上敌对的谬误提示信息。凋谢接口层要将异样解决成错误码和错误信息形式返回。
全局异样解决思路

前文说到,ServiceManagerController 层,是要有异样解决的。而这些须要解决的异样,往往能够划分为几类,不同的每类异样做对应的对立解决。例如:须要反馈给用户的异样(如:账号/明码谬误)、程序自行处理的异样(如:数据库锁抵触,乐观锁CAS的主动重试)、无非解决的服务器异样(如:数据库宕机、网络稳定等)... 。

针对这类能够对立分类解决的异样,倡议编写全局异样解决的办法来实现,具体实现办法有上面三个步骤:

  1. 自定义异样类:应用对立的异样,也能够自定义异样类,如“须要反馈给用户的异样”能够自定义业务异样类,手动抛出提醒语。
  2. 抛出异样:因为有了对立解决,就须要标准化的抛出异样。
  3. 全局异样解决:对于异样的解决,总不能在每个办法处try catch,能够利用面向切面编程的思维做全局异样解决。常见的全局异样解决形式有:@ControllerAdviceAOP过滤器拦截器等。不过要留神它们的执行程序为(ServletContextListener> Filter > Interception > AOP > 具体执行的办法 > AOP > @ControllerAdvice > Interception > Filter > ServletContextListener)。