# 代驾备份
**Repository Path**: romesum/backup-for-proxy-driving
## Basic Information
- **Project Name**: 代驾备份
- **Description**: 代驾备份
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2024-08-18
- **Last Updated**: 2024-08-18
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Java微服务畅行无忧打车项目
#### 介绍
基于Java微服务项目开发的代驾项目,包含司机端,管理端,用户端
畅行无忧是一个专为代驾司机而打造的服务平台,帮助司机用户提高接单速度,工作效率更高,赚钱更轻松.中国代驾行业快捷、安全、值得信赖的专业酒后代驾、商务代驾、长途代驾服务商。专业的驾驶服务平台,安心代驾实现智能计费、透明消费;为实现就近调度的原则,下单后,反应迅速,无需您长时间等待,服务人员会将您和您的车安全舒适地送回家。应用包括:平台端,司机端,乘客端,采用Alibaba微服务架构进行开发。
#### 软件架构
软件架构说明

2.业务流程
项目包括:`后台管理平台 , 司机端 , 用户端` 三个部分。用户端提供需要请代驾的用户进行注册,下单,支付等功能,司机端提供司机师傅注册入驻和资料认证,接单等功能,管理平台进行整体项目管理。项目核心业务流程如下

应用架构

4.技术架构
项目采用市场流行的SpringCloudAlibaba微服务架构, 管理系统采用VUE3.0 , 司机和用户端采用uniapp小程序开发。后端技术架构如下

二.后台项目搭建
注意:在搭建微服务之前,请安装好 nacos ,zipkin , sentinel ,rocketmq , elk 等组件 ,安装方式见:docker中间件安装 文档
1.项目规划
#### 1.1.项目结构
项目采用SpringBoot父子工程结构,SpringBoot和SpringCloud的依赖交给父工程去声明,公共的依赖在父工程中引入。为了方便管理把同一类子工程进行分类,形成三层结构,
```java
changxingwuyou //顶层,做依赖声明,版本管理,公共依赖导入
|---------changxingwuyou-basic //第二层,包含代码生成器和通用工具或公共代码(全局可用)
|---------changxingwuyou-code-gen //第三层,代码生成器
|---------changxingwuyou-common //第三层,通用工具
|---------changxingwuyou-service //第二层,包含多个微服务项目
|---------changxingwuyou-service-driver //第三层,司机微服务
|---------changxingwuyou-service-customer //第三层,乘客微服务
|---------changxingwuyou-starter //第二层,包含多个通用组件封装,比如:微信登录,短信
|---------changxingwuyou-starter-wechat //第三层,微信通用组件
|---------changxingwuyou-starter-map //第三层,地图通用组件
|---------changxingwuyou-api //第二层,包含多个远程API接口
|---------changxingwuyou-api-uaa //第三层,认证中心远程API
|---------changxingwuyou-api-pay //第三层,支付服务远程API
|---------changxingwuyou-support //第二层,包含多个基本支撑服务,比如网关,监控等
|---------changxingwuyou-gateway //第三层,服务网关
|---------changxingwuyou-monitor //第三层,服务监控
|---------changxingwuyou-ui //第二层,包含多个UI界面
|---------changxingwuyou-ui-admin //第三层,管理系统UI-vue
|---------changxingwuyou-ui-driver //第三层,司机端小程序Uniapp
|---------changxingwuyou-ui-customer //第三层,乘客端小程序Uniapp
```
1.2.端口规划
| 序号 | 服务名 | 端口 | 简介 |
| ---- | ------------------------------- | ----- | ---------- |
| 1 | changxingwuyou-gateway | 10010 | 服务网关 |
| 2 | changxingwuyou-service-driver | 10020 | 司机服务 |
| 3 | changxingwuyou-service-uaa | | 认证中心 |
| 4 | changxingwuyou-service-admin | | 管理服务 |
| 5 | changxingwuyou-service-common | | 公共服务 |
| 6 | changxingwuyou-service-customer | | 客户服务 |
| 7 | changxingwuyou-service-rule | | 费用规则 |
| 8 | changxingwuyou-service-order | | 订单服务 |
| 9 | changxingwuyou-service-pay | | 支付服务 |
| 10 | changxingwuyou-service-bigdata | | 大数据服务 |
| 11 | changxingwuyou-monitor | | 监控服务 |
| 12 | changxingwuyou-service-message | | 消息中心 |
上面只是部分微服务
1.3.分包规范
分包规范说的就是,类应该放在那些包下
```xml
com.changxingwuyou
|---启动类
|---controller
|---app
|---manager
|---pojo
|---app
|---dto
|---vo
|---manager
|---dto
|---vo
|---domain
|---properties
|---service
|---impl
|---mapper
```
远程API分包规范
com.changxingwuyou
|-----remote
|------api
|------fallback
|------impl
|------bo
|------param
|------result
1.4.版本管理
我们以SpringBoot 父子项目来做,根据上图搭建好项目基础架构,父工程导入公共依赖,其中包括对SpringBoot,SpringCloud的声明和一些公共依赖及插件
| JDK | SpringBoot | SpringCloud | SpringCloudAlibaba |
| ----- | ---------- | ----------- | ------------------ |
| jdk17 | 2.6.13 | 2021.0.5 | 2021.0.5.0 |
> 注意:编译版本一定要用 jdk17,所有的项目都是
```xml
17
17
UTF-8
1.0-SNAPSHOT
1.2.50
4.12
5.4.0
2021.0.5.0
2021.0.5
2.7
5.2
2.2.8.RELEASE
4.3.0
2.6.7
4.5.13
3.3.1
8.0.33
1.1.23
2.1
3.3.1.tmp
1.5.5.Final
1.37.0
2.6.1
5.6.155
2.2.3
3.16.1
5.0.0-HBase-2.0
org.springframework.boot
spring-boot-starter-parent
2.6.13
org.projectlombok
lombok
com.alibaba
fastjson
${fastjson.version}
org.springframework.boot
spring-boot-starter-test
junit
junit
${junit.version}
test
org.apache.commons
commons-lang3
cn.hutool
hutool-all
${hutool.version}
com.alibaba.cloud
spring-cloud-alibaba-dependencies
${cloud.alibaba.version}
pom
import
org.springframework.cloud
spring-cloud-dependencies
${spring.cloud.version}
pom
import
fccar-dev
fccar-dev
fccar-dev
127.0.0.1:8848
fccar-test
fccar-test
fccar-test
127.0.0.1:8848
fccar-prod
fccar-prod
fccar-prod
127.0.0.1:8848
src/main/resources
true
org.apache.maven.plugins
maven-resources-plugin
${maven-resources-plugin.version}
@
false
org.apache.maven.plugins
maven-surefire-plugin
${maven-surefire-plugin.version}
org.apache.maven.plugins
maven-compiler-plugin
${maven-compiler-plugin.version}
org.springframework.boot
spring-boot-configuration-processor
${springboot.version}
org.projectlombok
lombok
${lombok.version}
org.mapstruct
mapstruct-processor
${mapstruct.version}
```
注意:在父pom中指定了profile ,分别指定了 dev 开发环境,test测试环境 ,prod生成环境 的profile,其中的namespace会和nacos上的namespace对应上。如下:
1.5.nacos规范
注意:我们使用dev环境的配置文件,需要在nacos面板创建dev,test,prod三个 namespace,如下

然后在配置列表 - dev 环境下去创建配置文件,如下

3.网关搭建
步骤回顾
```shell
1.搭建项目
2.导入依赖(version管理)
3.启动类编写
4.application.yaml编写(端口,服务名,注册地址)
5.启动测试
6.nacos创建命名空间
7.nacos创建配置-拷贝本地配置
8.微服务创建bootstrap.yaml(配置地址,命名空间,前缀,后缀,分组,共享配置)
9.nacos创建共享配置文件
10.启动测试
```
#### 3.1.导入依赖
第一步:给网关服务 itsource-drive-gateway 的pom.xml加入依赖, 网关需要加入nacos的服务发现和配置管理的依赖,然后是sentinel的依赖,然后再加上swagger的依赖和gateway的基础依赖,如下:
```xml
17
17
UTF-8
org.springframework.cloud
spring-cloud-starter-loadbalancer
org.springframework.cloud
spring-cloud-starter-bootstrap
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
org.springframework.cloud
spring-cloud-starter-gateway
```
#### 3.2.编写启动类
第四步:编写启动类,启动测试
```java
//网关
@SpringBootApplication
public class GatewayStarter {
public static void main(String[] args) {
SpringApplication.run(GatewayStarter.class , args);
}
}
```
#### 3.3.编写配置文件
第二步:编写application.yaml配置文件,主要是nacos注册地址,端口,服务名,路由配置,跨域配置
```yaml
server:
port: 10010
spring:
cloud: #注册到Nacos
gateway:
httpclient:
connect-timeout: 10000
response-timeout: 10000 # 超时时间
discovery:
locator:
enabled: false # 开启自动路由
lower-case-service-id: true #服务名小写
routes: #路由配置
- id: it-fccar-bigdata #指定服务名
uri: lb://it-fccar-service-bigdata
#去注册中心找这个服务名
predicates: #断言,匹配访问的路径
- Path=/fccar/bigdata/** #服务访问路径
filters:
- StripPrefix=2
- id: it-fccar-common #指定服务名
uri: lb://it-fccar-service-common
#去注册中心找这个服务名
predicates: #断言,匹配访问的路径
- Path=/fccar/common/** #服务访问路径
filters:
- StripPrefix=2
- id: it-fccar-order #指定服务名
uri: lb://it-fccar-service-order
#去注册中心找这个服务名
predicates: #断言,匹配访问的路径
- Path=/fccar/order/** #服务访问路径
filters:
- StripPrefix=2
- id: it-fccar-coustomer #指定服务名
uri: lb://it-fccar-service-customer
#去注册中心找这个服务名
predicates: #断言,匹配访问的路径
- Path=/fccar/customer/** #服务访问路径
filters:
- StripPrefix=2
- id: it-fccar-uaa #指定服务名
uri: lb://it-fccar-service-uaa
#去注册中心找这个服务名
predicates: #断言,匹配访问的路径
- Path=/fccar/uaa/** #服务访问路径
filters:
- StripPrefix=2
- id: it-fccar-driver #指定服务名
uri: lb://it-fccar-service-driver
#去注册中心找这个服务名
predicates: #断言,匹配访问的路径
- Path=/fccar/driver/** #服务访问路径
filters:
- StripPrefix=2
- id: it-fccar-pay #指定服务名
uri: lb://it-fccar-service-pay
#去注册中心找这个服务名
predicates: #断言,匹配访问的路径
- Path=/fccar/pay/** #服务访问路径
filters:
- StripPrefix=2
- id: it-fccar-system #指定服务名
uri: lb://it-fccar-service-system
#去注册中心找这个服务名
predicates: #断言,匹配访问的路径
- Path=/fccar/system/** #服务访问路径
filters:
- StripPrefix=2
globalcors: #跨域配置
cors-configurations:
'[/**]':
allowedOriginPatterns: "*"
allow-credentials: true
allowed-headers: "*"
allowedMethods:
- GET
- POST
- DELETE
- PUT
- PATCH
- OPTIONS
- HEAD
- CONNECT
- TRACE
```
为了方便:这里把可能涉及到的微服务的路由都配置好了
#### 3.4.nacos管理配置
第三步:上nacos管理界面创建配置文件,为了方便管理,我这里创建了一个dev的命名空间

然后再配置管理列表中,在dev命名空间下创建配置文件changxingwuyou-gateway.yaml,内容就是application.yaml中的内容

在项目中创建bootstrap.yaml文件,内容如下
spring:
application:
name: "@artifactId@"
cloud:
nacos:
config:
server-addr: 172.168.100.158:8848 #自动取pom中的addr
namespace: fccar-dev #自动取pom中的namespace
username: nacos
password: nacos
name: changxingwuyou-gateway #changxingwuyou-gateway
file-extension: yaml
shared-configs: #公共配置
- data-id: changxingwuyou-shard.yaml
公共配置管理
注意:这里使用到了 shared-configs 共享配置,意味着我们还需要在nacos的dev命名空间中创建一个it-drive-shared-dev.yaml 的配置文件用来配置公共的一些内容,比如redis的配置,MQ的配置等等

注意:如果服务启动端口启动8080,说明配置文件未获取到,检查:配置文件名,后缀,环境,命名空间ID等
4.多环境配置
第一种是直接使用SpringBoot的多环境配置方案,如下:
- 在启动项目的时候,可以通过 java -jar 项目.jar --spring.profiles.active=dev 来激活不同的环境
```yaml
spring:
profiles:
active: dev
application:
name: changxingwuyou-gateway
---
spring:
config:
activate:
on-profile: dev
cloud:
nacos:
config:
server-addr: 172.168.100.158:8848
namespace: fccar-dev
name: ${spring.application.name}
file-extension: yaml
username: nacos
password: nacos
shared-configs:
- data-id: changxingwuyou-shard.yaml
---
spring:
config:
activate:
on-profile: test
cloud:
nacos:
config:
server-addr: 172.168.100.158:8848
namespace: fccar-test
name: ${spring.application.name}
file-extension: yaml
username: nacos
password: nacos
shared-configs:
- data-id: changxingwuyou-shard.yaml
---
spring:
config:
activate:
on-profile: prod
cloud:
nacos:
config:
server-addr: 172.168.100.158:8848
namespace: fccar-prod
name: ${spring.application.name}
file-extension: yaml
username: nacos
password: nacos
shared-configs:
- data-id: changxingwuyou-shard.yaml
```
#### 4.2.通过pom-profile切换
这种方式我们可以通过在pom.xml中配置 profiles 来配置不同的环境,比如:
```xml
dev
dev
fccar-dev
172.168.100.158:8848
nacos
nacos
test
test
fccar-test
172.168.100.158:8848
nacos
nacos
prod
prod
fccar-prod
172.168.100.158:8848
nacos
nacos
src/main/resources
true
org.apache.maven.plugins
maven-resources-plugin
${maven-resources-plugin.version}
@
false
```
因为我们在顶层pom.xml中配置了 profiles 以及 maven-resources-plugin ,所以在yaml中可以使用 @xxxx@ 来动态取值 , 现在我们可以使用下面的方式来配置:
```yaml
spring:
application:
name: @artifactId@ #自动取模块名作为服务名
profiles:
active: @profiles.active@ #自动取pom中的profiles.active ,需要勾选环境
cloud:
nacos:
config:
server-addr: @nacos.addr@ #自动取pom中的addr ;
namespace: @nacos.namespace@ #自动取pom中的namespace
username: @nacos.username@
password: @nacos.password@
name: @artifactId@ #it-fccar-gateway
file-extension: yaml
shared-configs: #公共配置
- data-id: it-fccar-service-shard.yaml
```
**profiles切换**
注意:@profiles.active@ 是自动指定profiles,需要再maven中选择对应的profiles来进行切换

注意:如果需要打包项目时激活环境,只需要使用该命令 : mvn package -Pdev
5.司机服务搭建
步骤回顾
```yaml
1.搭建项目
2.导入依赖(version管理,抽取到父模块)
3.启动类编写
4.application.yaml编写(端口,服务名,注册地址)
5.启动测试
6.nacos创建配置-拷贝本地配置
8.微服务创建bootstrap.yaml(配置地址,命名空间,前缀,后缀,分组,共享配置)
9.启动测试
```
#### 5.1.公共模块
公共模块中存放整个项目会用到的一些公共的代码,比如:工具类,基础实体对象,基础query对象,全局异常,全局常量等等。在changxingwuyou-basic下创建模块changxingwuyou-common ,然后从课件中拷贝相关的代码到模块中,根据需要导入相关依赖。
#### 5.2.微服务依赖管理
我们需要创建很多的微服务,而这些微服务都有相同的依赖比如:nacos , mybatisplus等等,所以我们可以把微服务通用的依赖放到微服务的父工程中(注意是微服务的父工程不是最顶层工程),如下:
```xml
17
17
UTF-8
org.springframework.cloud
spring-cloud-starter-loadbalancer
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.cloud
spring-cloud-starter-bootstrap
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
org.springframework.boot
spring-boot-starter-web
jakarta.validation
jakarta.validation-api
cn.itsource
it-fccar-common
${fccar-version}
```
#### 5.2.司机服务
一步:搭建司机服务 changxingwuyou-service-driver,导入需要的依赖,包括:nacos服务发现,nacos配置管理,sentinel,web基础依赖,数据库驱动,和swagger依赖,这些依赖都会作为一个服务最基础的依赖,我们可以选择把依赖放到期父工程:`changxingwuyou-service-driver/pom.xml`中,这样所有的微服务都有这些依赖了
#### 5.3.创建配置
第二步,为司机服务编写配置文件,并推送配置到nacos。在nacos的dev环境中创建changxingwuyou-driver.yaml配置文件内容如下:
```yaml
server:
port: 10020
spring:
application:
name: @artifactId@
```
当我们把一些公共的配置内容抽取到application-dev.yaml后,微服务的配置文件内容就会比较少了,然后再司机服务resources中创建bootstrap.yaml取拉取配置
#### 5.4.司机本地配置
在nacos中创建配置文件,然后再本地创建bootstrap.yaml,通过nacos拉取配置文件,和gateway一样。内容如下
```yaml
spring:
application:
name: @artifactId@ #自动取模块名作为服务名
profiles:
active: @profiles.active@ #自动取pom中的profiles.active ,需要勾选环境
cloud:
nacos:
config:
server-addr: @nacos.addr@ #自动取pom中的addr ;
namespace: @nacos.namespace@ #自动取pom中的namespace
username: @nacos.username@
password: @nacos.password@
name: @artifactId@ #it-fccar-gateway
file-extension: yaml
shared-configs: #公共配置
- data-id: it-fccar-shard.yaml
```
### **6.MybatisPlus代码生成**
在真实项目开发中我们的服务模块,一般都要进行数据库操作,并且每个domain都有crud,需多次写重复代码。我们使用MybatisPlus,就不用写重复代码,并且还有模板的功能,可以一键生成daomin,query,mapper接口,mapper.xml,service,controller,非常好用。
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。他有如下特点:
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 XML 热加载Mapper 对应的 XML支持热加载对于简单的 CRUD 操作甚至可以无 XML 启动
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 支持关键词自动转义:支持数据库关键词(order、key......)自动转义,还可自定义关键词
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 内置性能分析插件:可输出 Sql 语句以及其执行时间建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete,update 操作智能分析阻断也可自定义拦截规则,预防误操作
- 内置 Sql 注入剥离器:支持 Sql 注入剥离,有效预防 Sql 注入攻击
也就是说,我们不仅可以使用MybatisPlus来代替mybatis,还可以使用MybatisPlus给我们生产基础的CRUD代码,下面是一个

下面我们就来使用一下MybatisPlus的强大功能
#### **6.1.代码生成器**
第一步,创建代码生成器模块,itsource-drive-generator-code ,导入需要的依赖
```xml
17
17
UTF-8
com.baomidou
mybatis-plus-boot-starter
3.3.1
com.baomidou
mybatis-plus-generator
3.3.1.tmp
org.apache.velocity
velocity-engine-core
2.1
mysql
mysql-connector-java
8.0.18
```
第二步:把准备好的模板 controller.java.vm ; query.java.vm 拷贝到resources /templates目录下

第三步:创建一个resources/config-driver.properties文件,内容如下
```properties
OutputDir=D:/devlop/code_space/it-fccar/it-fccar/it-fccar-service/it-fccar-service-driver/src/main/java
OutputDirXml=D:/devlop/code_space/it-fccar/it-fccar/it-fccar-service/it-fccar-service-driver/src/main/resources
author=墨家巨子
parent=cn.itsource
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///fccar-drver?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
jdbc.user=root
jdbc.pwd=123456
```
第四步:使用下面代码来配置代码生成器和执行main方法生成代码
```java
package cn.itsource;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ResourceBundle;
public class Genterator {
public static void main(String[] args) {
//读取配置文件
ResourceBundle rb = ResourceBundle.getBundle("config-driver");
//业务代码输出路径
String outputDir = rb.getString("OutputDir");
//sql映射文件输出路径
String outputDirXml = rb.getString("OutputDirXml");
//作者
String author = rb.getString("author");
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
// 代码输出位置
gc.setOutputDir(outputDir);
// 作者
gc.setAuthor(author);
// 打开代码生成目录
gc.setOpen(true);
//生成 resultMap
gc.setBaseResultMap(true);
//生成查询列明
gc.setBaseColumnList(true);
//日期类型
gc.setDateType(DateType.ONLY_DATE);
//ID使用雪花算法
gc.setIdType(IdType.ASSIGN_ID);
//添加接口文档注解
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
// 数据库类型
dsc.setDbType(DbType.MYSQL);
dsc.setTypeConvert(new MySqlTypeConvert());
// 连接属性
dsc.setDriverName(rb.getString("jdbc.driver"));
dsc.setUsername(rb.getString("jdbc.user"));
dsc.setPassword(rb.getString("jdbc.pwd"));
dsc.setUrl(rb.getString("jdbc.url"));
mpg.setDataSource(dsc);
// 表策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setTablePrefix(new String[] { "t_" });// 此处可以修改为您的表前缀
strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
strategy.setInclude(new String[]{
"t_driver",
"t_driver_aggrement",
"t_driver_auth_material",
"t_driver_material_auth_log",
"t_driver_setting",
"t_driver_summary",
"t_driver_wallet",
"t_driver_wallet_flow"
}); // 需要生成的表
//使用lombok
strategy.setEntityLombokModel(true);
strategy.setEntitySerialVersionUID(true);
//乐观锁字段
strategy.setVersionFieldName("version");
//逻辑删除字段
strategy.setLogicDeleteFieldName("deleted");
//domain的父类
//strategy.setSuperEntityClass("cn.itsource.pojo.BaseDomain");
//controller的父类
//strategy.setSuperControllerClass("cn.itsource.controller.BaseController");
//生成注解
strategy.setEntityTableFieldAnnotationEnable(true);
strategy.setEntitySerialVersionUID(true);
mpg.setStrategy(strategy);
// 包配置
PackageConfig pc = new PackageConfig();
//基础路径 cn.xxx
pc.setParent(rb.getString("parent"));
//controller的包
pc.setController("controller.manager");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setEntity("pojo.domain");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 注入自定义配置,可以在 VM 中使用 cfg.abc 【可无】
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
this.setMap(new HashMap());
}
};
//文件生成配置
List focList = new ArrayList();
//controller的输出配置
focList.add(new FileOutConfig("/templates/controller.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
//合并好的内容输出到哪儿?
return outputDir+ "/cn/itsource/controller/manager/" + tableInfo.getEntityName() + "Controller.java";
}
});
// 调整 domain 生成目录演示
focList.add(new FileOutConfig("/templates/entity.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return outputDir+ "/cn/itsource/pojo/domain/" + tableInfo.getEntityName() + ".java";
}
});
// 调整 xml 生成目录演示
focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return outputDirXml+ "/cn/itsource/mapper/" + tableInfo.getEntityName() + "Mapper.xml";
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 自定义模板配置,可以 copy 源码 mybatis-plus/src/main/resources/templates 下面内容修改,
// 放置自己项目的 src/main/resources/templates 目录下, 默认名称可以不配置,也可以自定义模板名称
TemplateConfig tc = new TemplateConfig();
tc.setService("/templates/service.java.vm");
tc.setServiceImpl("/templates/serviceImpl.java.vm");
tc.setMapper("/templates/mapper.java.vm");
tc.setEntity(null);
tc.setController(null);
tc.setXml(null);
// 如上任何一个模块如果设置 空 OR Null 将不生成该模块。
mpg.setTemplate(tc);
// 执行生成
mpg.execute();
}
}
```
#### 6.2.司机整合MybatisPlus
第一步:导入mybatisplus的依赖
```xml
com.baomidou
mybatis-plus-boot-starter
3.3.1
mysql
mysql-connector-java
8.0.33
com.alibaba
druid-spring-boot-starter
1.1.23
```
第二步:加入mybatisPlus的yaml配置
```yaml
server:
port: 10020
spring:
application:
name: @artifactId@
# mybaits-plus数据源配置
datasource:
username: root
password: 123456
url: jdbc:mysql:///fccar-driver?serverTimezone=Asia/Shanghai&characterEncoding=utf8
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource # 配置阿里的连接池
druid: # Druid 【监控】相关的全局配置
# 配置初始化大小、最小、最大
initial-size: 5
minIdle: 10
max-active: 20
# 配置获取连接等待超时的时间(单位:毫秒)
max-wait: 60000
########### 启用内置过滤器(第一个 stat必须,否则监控不到SQL)##########
filters: stat,wall,log4j2
web-stat-filter:
enabled: true
stat-view-servlet:
enabled: true
allow: # 设置白名单,不填则允许所有访问
url-pattern: /druid/*
login-username: fccar # 控制台管理用户名和密码
login-password: fccar
filter:
stat:
enabled: true
log-slow-sql: true # 慢 SQL 记录
slow-sql-millis: 2000
merge-sql: true
wall:
config:
multi-statement-allow: true
# mybatis-plus配置
mybatis-plus:
type-aliases-package: cn.itsource.domain,cn.itsource.query #别名包扫描
mapper-locations: classpath:cn/itsource/mapper/*Mapper.xml #SQL映射文件扫描
global-config:
db-config: #设置逻辑删除,通过把delete字段改成0而不是直接删除数据
logic-not-delete-value: 0
logic-delete-value: 1
configuration:
map-underscore-to-camel-case: true # 开启驼峰命名转换法
cache-enabled: false #禁用缓存
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 配置日志,在控制台输出SQL
```
第三步:编写mybatisPlus的配置类
```java
@Configuration
//扫描Mybatis的mapper映射器
@MapperScan("cn.itsource.mapper")
public class MybatisPlusConfig {
//分页插件配置对象,Mybatis-plus需要此配置对象
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
//乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
}
}
```
- OptimisticLockerInterceptor : mybatisplus 内置乐观锁拦截器,在update数据的时候会自动校验version
注意:对于mybatisplus这种配置类,所有的微服务都需要,可以抽取到一个 changxingwuyou-start-core 通用组件中,要使用导入其依赖即可。

#### 6.3.乐观锁理解
乐观锁的含义是:在业务执行的过程中不加锁,当数据提交到数据库的时候通过版本号或者时间戳等方式去判断当前数据是否发生并发修改,如果发生并发修改就取消操作或回滚事务,从而保证线程并发安全。如下

MybatisPlus提供了update时进行乐观锁自动校验功能,我们只需要在表中提供 version字段,然后配置好MybatisPlus的乐观锁插件即可。
第一步:数据库配置乐观锁

第二步:配置乐观锁插件

第三步骤:实体类标记乐观锁字段

当我们执行 update时,乐观锁就会生效
7.整合Swagger文档
#### 7.1.什么是swagger 及 Knife4j
在企业中为了约定项目的后端接口,通常会使用doc编写好接口文档,然后交给前后端开发人员去进行开发,如果每次都是通过人工把接口文档编写出来是一件非常耗时的工作,比如我这里提供了一份微信支付的接口文档案例:
[https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml) ,
Swagger是一款文档生成工具,Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务,可以根据我们的controller接口自动生接口文档,并且可以直接通过swagger发请求测试代码,前端开发人员也可以通过swagger接口文档进行调试,非常方便。
Knife4j 的前身是 swagger-bootstrap-ui,是 springfox-swagger-ui 的增强 UI 实现。swagger-bootstrap-ui 采用的是前端 UI 混合后端 Java 代码的打包方式,在微服务的场景下显得非常臃肿,改良后的 Knife4j 更加小巧、轻量,并且功能更加强大。

7.2.微服务整合Knife4j
第一步:导入Knife4j起步依赖
```xml
com.github.xiaoymin
knife4j-openapi3-spring-boot-starter
4.3.0
```
加入knife4j的文档配置
```yaml
# springdoc-openapi项目配置
springdoc:
swagger-ui:
path: /doc.html
tags-sorter: alpha
operations-sorter: alpha
api-docs:
path: /v3/api-docs/default
# knife4j的增强配置,不需要增强可以不配
knife4j:
enable: true
setting:
language: zh_cn
swagger-model-name: 飞驰专车
basic:
enable: true
username: fccar
password: fccar
```
注意:如果启动报错可能是SpringFox 与 SpringBoot 2.6.x 不兼容的问题,加入下面配置
```yaml
spring:
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER # 解决 SpringFox 与 SpringBoot 2.6.x 不兼容的问题,参见 SpringFoxHandlerProviderBeanPostProcessor 类
```
启动测试:/doc.html
如果要做文档定制可以加入注解
标记接口
```java
@Tag(name = "司机API",description = "司机功能接口") :标记controller
```
标记方法
```json
@Operation( summary= "司机下线",description = "司机下线停止接单")
@Parameters({
@Parameter(name = "orderNo",description = "订单号")
})
```
标记实体类
```java
@Schema(name = "Employee对象", description = "员工")
public class Employee implements Serializable {
@Schema(name = "id", description = "")
//....
private Long id;
@Schema(name = "realName", description = "姓名")
//....
private String realName;
```
#### 7.3.网关整合swagger
网关整合swagger就是把各个微服务的swagger文档统一起来,提供一个统一的访问界面,这样就没必要到每个微服务中去访问接口文档了,网关
```xml
com.github.xiaoymin
knife4j-gateway-spring-boot-starter
4.3.0
```
加入gateway的文档配置
```java
knife4j:
gateway:
# ① 第一个配置,开启gateway聚合组件
enabled: true
# ② 第二行配置,设置聚合模式采用discover服务发现的模式
strategy: discover
discover:
# ③ 第三行配置,开启discover模式
enabled: true
# ④ 第四行配置,聚合子服务全部为Swagger2规范的文档
version: OpenAPI3
```
启动测试:/doc.html
### 8.异常处理
#### 8.1.自定义异常
需求说明:为了不在controller编写大量的try-catch代码,需要进行统一异常处理,同时要进行错误信息以及错误码的统一管理,建议使用枚举进行错误码封装。同时要求系统支持JS303校验规则。
在开发中,为了保证业务的正确性我们会对业务参数进行校验,不满足要求的参数采用throw new XxxException 来抛出异常,阻止程序继续执行,比如:用户名不可为空,密码错误等。这类异常是需要展示给用户看的,还有一类是程序抛出来的一些未知的异常,如:SQL异常,空指针异常等等,这类异常不能直接抛给用户,而是应该统一捕获后,封装成统一的错误提示返回给用户,如“系统内部异常啦”。
为了把我们手动抛出的异常和系统内部出现的异常区分开,我们需要抛出自定义的异常。通过继承RuntimeException自定义异常。
```java
public class GlobalException extends RuntimeException{
//把异常信息传递给RuntimeException
public GlobalException(String message){
super(message);
}
}
```
在业务中对可预知的异常信息就可以使用 throw new GlobalException("异常信息"),把异常向上抛出去了
```java
if(StringUtils.isEmpty(username)){
throw new GlobalException("用户名不可为空")
}
```
然后我们可以在controller中写try-catch进行异常捕获,分类处理
```java
@PutMapping
public JsonResult update(@RequestBody @Valid Driver driver){
try{
return JsonResult.success(driverService.updateById(driver));
}catch (GlobalException e){
//自定义异常,把错误信息抛给用户看
e.printStackTrace();
return JsonResult.error(e.getMessage());
}catch (Exception e){
//其他异常,统一处理
e.printStackTrace();
return JsonResult.error("系统异常了,请联系管理员");
}
}
```
#### 8.2.统一异常捕获
上面捕获异常的方式有一个很大的问题:大量重复代码,拉低了代码的可读性和增加了时间成本。为了节约开发成本,增加代码的可读性,统一捕获异常,我们使用AOP的思想,解决在controller中大量try-catch重复代码。:在SpringMVC中提供了2个注解可以帮我们完成该项工作:@RestControllerAdvice +@ExceptionHandler
在base-common公共模块,创建全局异常捕获类,通过两个注解来实现异常统一处理
- @RestControllerAdvice :贴在类上,表示对Controller的增强,他会被启动类自动识别
- @ExceptionHandler(value = Exception.class) : 贴在方法上,表示捕获指定的异常。针对不同的异常可以写不同的处理方法。
```java
//全局异常捕获
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
// 捕捉Exception异常,返回统一的错误提示
@ExceptionHandler(value = Exception.class)
public JSONResult exceptionErrorMethod(Exception e, HttpServletRequest request) {
e.printStackTrace();
log.error("发生异常错误,请求地址:{},异常信息{}", request.getRequestURI(), e.getMessage());
return JSONResult.error("服务器内部异常,请联系管理员");
}
// 捕捉GlobalException异常,把错误日志抛给用户
@ExceptionHandler(value = GlobalException.class)
public JSONResult globalCustomMethod(GlobalException e, HttpServletRequest request) {
e.printStackTrace();
log.error("发生异常错误,请求地址:{},异常信息:{}", request.getRequestURI(), e.getMessage());
return JSONResult.error(e.getMessage());
}
}
```
这样一来,我们便可以对不用的异常分开捕获,如果是我们能预知的异常:用户名不可空 之类的就可以使用GlobalException,然后统一捕获之后把异常返回给用户。而其他系统内部异常全部封装为同一个异常提示信息返回。那么在controller方法中不再需要处理异常了。
#### 8.3.断言工具
在我们对参数做判断的时候,会做很多的if判断,然后不满足就抛出异常,这样的代码写多了也是一个很耗时的工作
```java
if(StringUtils.isEmpty(username)){
throw new GlobalException("用户名不可为空")
}
if(StringUtils.isEmpty(password)){
throw new GlobalException("密码不可为空")
}
...
```
这种重复的代码我们可以进行抽取封装,让他变得更加简洁,这里我参考Spring的Assert断言工具,封装了一个工具AssertUtil类,上面代码可以这样改造:
```java
public class AssertUtil {
public static void isNotEmpty(String text, String message) {
if (text == null || text.trim().length() == 0) {
throw new GlobalException(message);
}
}
}
```
其实就是把判断和抛出异常的代码封装到工具里面,这样的话我们判断参数只需要这样写即可: 是不是变得很简单了呢
```java
AssertUtil.isNotEmpty(username,"用户名不可空")
AssertUtil.isNotEmpty(password,"密码不可空")
```
#### 8.4.错误码封装
系统中有大量的异常信息分散在代码各处,为了方便管理和维护我们可以使用枚举类方便的统一管理起来,那么我们在抛异常的时候就统一使用枚举中定义好的异常信息就可以了。
- code : 错误码
- message : 错误日志
```java
/** 系统错误码 **/
public enum ErrorCode {
SYSTEM_ERROR(500,"系统内部异常");
//错误码
private int code;
//错误信息
private String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message+"["+code+"]";
}
}
```
同时为了方便把错误信息和错误码封装到异常对象,我们可以对 GlobalException做改造,如下
```java
@Data
public class GlobalException extends RuntimeException{
//用来把错误码传递给JsonResult对象
private int code;
public GlobalException(String message,int code){
super(message);
this.code = code;
}
public GlobalException(ErrorCode errorCode){
super(errorCode.getMessage());
this.code = errorCode.getCode();
}
public GlobalException(String message){
super(message);
}
}
```
使用的时候更加简单了 : throw new GlobalException(ErrorCode.SYSTEM_ERROR) 即可。然后再统一异常处理类中可以把错误码设置给JsonResult相应给前端了
```java
// 捕捉Exception异常
@ExceptionHandler(value = GlobalException.class)
public JsonResult globalCustomMethod(GlobalException e, HttpServletRequest request){
e.printStackTrace();
log.error("发生异常错误,请求地址:{},异常信息:{}",request.getRequestURI(),e.getMessage());
return JsonResult.error(e.getMessage(),e.getCode());
}
```
#### 8.5.网关异常处理
Gateway网关是用的是webflux开发,而不是SpringMVC,所以之前那一套对网关就不合适了,Gateway是通过DefaultErrorWebExceptionHandler来处理异常,我们可以通过实现ErrorWebExceptionHandler接口,重写handle方法,替换掉框架默认的异常处理实现类DefaultErrorWebExceptionHandler
```java
package cn.itsource.exception;
import cn.itsource.result.JSONResult;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.netty.ByteBufMono;
@Component
@Order(-1)
@Slf4j
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
@Override
public Mono handle(ServerWebExchange exchange, Throwable ex) {
//拿到请求和响应
ServerHttpResponse response = exchange.getResponse();
ServerHttpRequest request = exchange.getRequest();
log.error("ErrorWebExceptionHandler 发生异常 {} ,{} ,{}", request.getURI(), request.getMethod(), ex.getMessage());
//处理异常,包装结果
JSONResult jsonResult = null;
if (ex instanceof ResponseStatusException) {
jsonResult = JSONResult.builder().success(false)
.message(((ResponseStatusException) ex).getReason())
.code(((ResponseStatusException) ex).getStatus().value())
.build();
} else {
jsonResult = JSONResult.builder().success(false).message(ex.getMessage()).code(500).build();
}
//以JSON方式把异常写出去
DataBuffer dataBuffer = response.bufferFactory()
.allocateBuffer().write(JSON.toJSONString(jsonResult).getBytes());
response.setStatusCode(HttpStatus.OK);
//基于流形式
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response.writeAndFlushWith(Mono.just(ByteBufMono.just(dataBuffer)));
}
//处理系统异常,兜底处理所有的一切
@ExceptionHandler(value = Exception.class)
public JSONResult defaultExceptionHandler(ServerWebExchange exchange,Throwable ex) {
ServerHttpRequest request = exchange.getRequest();
log.error("全局异常捕获 {} ,{} ,{}", request.getURI(), request.getMethod(), ex);
return JSONResult.builder().success(false).message(ex.getMessage()).code(200).build();
}
}
```
### 9.JSR303对象校验
参数校验是我们程序开发中必不可少的过程。用户在前端页面上填写表单时,前端js程序会校验参数的合法性,当数据到了后端,为了防止恶意操作,保持程序的健壮性,后端同样需要对数据进行校验。后端参数校验最简单的做法是直接在业务方法里面进行判断,当判断成功之后再继续往下执行。但这样带给我们的是代码的耦合,冗余。当我们多个地方需要校验时,我们就需要在每一个地方调用校验程序,导致代码很冗余,且不美观。那么如何优雅的对参数进行校验呢?JSR303就是为了解决这个问题出现的,本篇文章主要是介绍 JSR303,Hibernate Validator 等校验工具的使用,以及自定义校验注解的使用。
```xml
org.hibernate.validator
hibernate-validator
6.0.17.Final
```
#### 8.1.Bean Validation 注解
Bean Validation 中内置的 constraint
| **Constraint** | **详细信息** |
| --------------------------- | -------------------------------------------------------- |
| @Null | 被注释的元素必须为 null |
| @NotNull | 被注释的元素必须不为 null |
| @AssertTrue | 被注释的元素必须为 true |
| @AssertFalse | 被注释的元素必须为 false |
| @Min(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
| @Max(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
| @DecimalMin(value) | 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 |
| @DecimalMax(value) | 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 |
| @Size(max, min) | 被注释的元素的大小必须在指定的范围内 |
| @Digits (integer, fraction) | 被注释的元素必须是一个数字,其值必须在可接受的范围内 |
| @Past | 被注释的元素必须是一个过去的日期 |
| @Future | 被注释的元素必须是一个将来的日期 |
| @Pattern(value) | 被注释的元素必须符合指定的正则表达式 |
Hibernate Validator 附加的 constraint
| **Constraint** | **详细信息** |
| -------------- | -------------------------------------- |
| @Email | 被注释的元素必须是电子邮箱地址 |
| @Length | 被注释的字符串的大小必须在指定的范围内 |
| @NotEmpty | 被注释的字符串的必须非空 |
| @Range | 被注释的元素必须在合适的范围内 |
#### 9.2.对象校验
第一步:字段校验,字段校验可以有下面几种用法
- 在参数实体类的字段上注解,
```
@NotEmpty(message = "手机不可为空")
private String phone;
```
- 在方法的参数前也可以加校验注解
```
@RequestMapping("/user/add")
public JSONResult add(@NotEmpty(message = "不可为空") String username){
...省略...
}
```
第二步:在参数实体类前注解 ,@Valid 或者 @Validated都可以标识该类需要进行校验,在类上也可以加该注解。举例
```
public JSONResult register(@RequestBody @Valid RegisterParamDto dto)
或者
```
#### 9.3.自定义校验注解【扩展】
当内置的校验注解不能满足我们时,我们可以选择自定义校验注解,比如:我们以自定义一个手机号校验的注解为例,第一步:定义校验注解
```
@Target( ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = PhoneValidator.class)////此处指定了注解的实现类为ListNotHasNullValidatorImpl
public @interface Phone {
String message() default "无效的手机格式";
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
}
```
第二步:仿照已有注解,定义校验器
- ConstraintValidator 是 Bean Validate 提供的 对象校验器的接口
- isValid : 该方法是用来做校验规则的方法,返回的boolean值代表是否校验通过
```
@Service
public class PhoneValidator implements ConstraintValidator {
//手机号正则
private static final String PHONE_REGEX = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(17[013678])|(18[0-9]))\\d{8}$";
@Override
public void initialize(Phone constraintAnnotation) {
//初始化
}
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
//对值进行手机号正则判断
Pattern p = Pattern.compile(PHONE_REGEX);
Matcher m = p.matcher(value);
return m.matches();
}
}
```
第三步:使用自定义的校验注解
```
@Phone
private String phone;
```
#### 9.4.分组校验【扩展】
如果同一个实体在不同的业务中做的校验规则也不一样,这个时候可以采用分组校验,比如:ID在添加逻辑中可以问空,在修改逻辑中则不可为空。1.定义不同的校验接口
```
/**
* 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型新增时的参数校验规则
*/
public interface PersonAddView {
}
/**
* 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型修改时的参数校验规则
*/
public interface PersonModifyView {
}
```
2.Model指明分组
```
public class Person {
private long id;
/**
* 添加groups 属性,说明只在特定的验证规则里面起作用,不加则表示在使用Deafault规则时起作用
*/
@NotNull(groups = {PersonAddView.class, PersonModifyView.class},
message = "添加、修改用户时名字不能为空", payload = ValidateErrorLevel.Info.class)
private String name;
@NotNull(groups = {PersonAddView.class}, message = "添加用户时地址不能为空")
private String address;
@Max(value = 30, groups = {PersonModifyView.class}, message = "姓名不能超过30岁")
private int age;
}
```
3.启用校验 , 此时启用校验和之前的不同,需要指明启用哪一组规则
```
/**
* 添加一个Person对象
* 此处启用PersonAddView 这个验证规则
* 备注:此处@Validated(PersonAddView.class) 表示使用PersonAndView这套校验规则,若使用@Valid 则表示使用默认校验规则,
* 若两个规则同时加上去,则只有第一套起作用
*/
@RequestMapping(value = "/person", method = RequestMethod.POST)
public void addPerson(@RequestBody @Validated({PersonAddView.class, Default.class}) Person person) {
System.out.println(person.toString());
}
/**
* 修改Person对象
* 此处启用PersonModifyView 这个验证规则
*/
@RequestMapping(value = "/person", method = RequestMethod.PUT)
public void modifyPerson(@RequestBody @Validated(value = {PersonModifyView.class}) Person person) {
System.out.println(person.toString());
}
```
上面就完成了一个微服务的搭建流程,我们可以先把下面几个微服务搭建起来