基于老SpringCloud工程
1. 业务需求
2. 新建父工程
- 打开IDEA, 点击File->New->Project:
- 配置注解生效
- 配置pom.xml
<packaging>pom</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hutool.version>5.8.22</hutool.version>
<lombok.version>1.18.26</lombok.version>
<mysql.version>8.0.11</mysql.version>
<swagger3.version>2.5.0</swagger3.version>
<mapper.version>4.2.3</mapper.version>
<fastjson2.version>2.0.40</fastjson2.version>
<spring.boot.version>3.2.4</spring.boot.version>
<spring.cloud.version>2023.0.3</spring.cloud.version>
<spring.cloud.alibaba.version>2023.0.1.2</spring.cloud.alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--SpringBoot集成jpa-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<!--Mysql数据库驱动8 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- fastjson2 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<!-- swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${swagger3.version}</version>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<!-- spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
dependencyManagement说明
Maven使用dependencyManagement元素来提供了一种管理依赖版本号的方式。 dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的,只有在子项目中写了该依赖项并且没有指定具体版本,才会从父项目中继承该项且version和scope都读取自父pom;
好处就是:
- 如果有多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号,这样当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要一个一个子项目的修改;
- 特别的,如果某个子项目需要另外的一个版本,只需要声明version就可,比较灵活。
3. 创建cloud-provider-payment8001子工程
- 选择父工程springcloud-base,右键New->Module:
- 修改pom.xml
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--SpringBoot通用依赖模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!--Mysql数据库驱动8 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!-- fastjson2 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 添加YML文件
在resource目录下添加application.yml:
server:
port: 8001
spring:
application:
name: cloud-payment-service
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.101.104:3306/db2024?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: root
password: 123456
jpa:
show-sql: true
database: mysql
database-platform: org.hibernate.dialect.MySQLDialect
hibernate:
ddl-auto: update
naming:
implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
physical-strategy: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy
- 添加启动类
package com.rocket.springcloud.provider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EnableJpaRepositories(basePackages = {"com.rocket.springcloud.provider.dao"})
@SpringBootApplication
public class PaymentMain {
public static void main(String[] args) {
SpringApplication.run(PaymentMain.class, args);
}
}
@EnableJpaRepositories是Spring Framework中用于启用JPA(Java Persistence API)仓库的注解:
basePackages(String[])
:指定了要扫描的包路径,用于查找JPA仓库接口。queryLookupStrategy()
:定义查找查询的策略。
4. 使用Jpa Buddy插件生成代码
JPA Buddy是面向使用JPA数据模型和相关技术(如Spring DataJPA,DB版本控制工具(Flyway,Liquibase),MapStruct等)的IntelliJ IDEA插件,该插件提供了可视化设计器、代码生成和其他检查功能。
4.1 安装Jpa Buddy插件
4.2 执行数据库初始化脚本
使用DBeaver客户端工具连接MySQL数据库,执行以下脚本:
DROP TABLE IF EXISTS `t_pay`;
CREATE TABLE `t_pay` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`pay_no` VARCHAR(50) NOT NULL COMMENT '支付流水号',
`order_no` VARCHAR(50) NOT NULL COMMENT '订单流水号',
`user_id` INT(10) DEFAULT '1' COMMENT '用户账号ID',
`amount` DECIMAL(8,2) NOT NULL DEFAULT '9.9' COMMENT '交易金额',
`deleted` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='支付交易表';
INSERT INTO t_pay(pay_no,order_no) VALUES('pay17203699','6544bafb424a');
4.3 生成数据库实体
创建包com.rocket.springcloud.provider.entity, 右键New->选择JPA Entity From DB: 进入可视化表实体面板,里面可以选择表和编辑实体名称Pay:
点击完成后生成Pay对象, 在Pay的类上加上@DynamicUpdate注解,更新的时候Hibernate会比较entity实体类中发生变化的字段值,只更新那些字段值发生变化的列。
@Getter
@Setter
@Entity
@Table(name = "t_pay")
@DynamicUpdate
public class Pay {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", columnDefinition = "int UNSIGNED not null")
private Long id;
@Size(max = 50)
@NotNull
@Column(name = "pay_no", nullable = false, length = 50)
private String payNo;
@Size(max = 50)
@NotNull
@Column(name = "order_no", nullable = false, length = 50)
private String orderNo;
@ColumnDefault("1")
@Column(name = "user_id")
private Integer userId;
@NotNull
@ColumnDefault("0.00")
@Column(name = "amount", nullable = false, precision = 8, scale = 2)
private BigDecimal amount;
@ColumnDefault("'0'")
@Column(name = "deleted", columnDefinition = "tinyint UNSIGNED not null")
private Short deleted;
@NotNull
@ColumnDefault("CURRENT_TIMESTAMP")
@Column(name = "create_time")
private Instant createTime;
@NotNull
@ColumnDefault("CURRENT_TIMESTAMP")
@Column(name = "update_time")
private Instant updateTime;
}
@DynamicUpdate
的作用并不是更新不为null字段,而是更新变化的字段, 解决办法:
- 自己写一个方法先findOne查出原有值再把不想变化的字段塞回要save()的对象中。
- 是使用SQL语句。 创建包com.rocket.springcloud.provider.dto, 右键New->选择DTO:
选择需要的属性和编辑类名,支持配置常见的校验,设置类名为PayDto:
生成的PayDto代码如下:
/**
* DTO for {@link com.rocket.springcloud.provider.entity.Pay}
*/
//@Value
@Setter
@Getter
public class PayDto implements Serializable {
Long id;
@NotNull
@Size(max = 50)
String payNo;
@NotNull
@Size(max = 50)
String orderNo;
Integer userId;
@NotNull
BigDecimal amount;
}
注释掉@Value
注解,改为@Setter
、@Gette
r,被
@Value标识的类不可被修改,不能与
@RequestBody`、jackson同时使用。
创建包com.rocket.springcloud.provider.dao, 右键New->选择SpringData JPA Respository: 选择我们要操作的Entity对象,设置类名为PayRespository:
点击完成生成, 添加@Repository注解使得加入Spring容器:
@Repository
public interface PayRepository extends JpaRepository<Pay, Long> {
}
5. 编写service\controller代码
创建包com.rocket.springcloud.provider.service, 编写PayService,PayServiceImpl:
public interface PayService {
public Pay add(Pay pay);
public int delete(Long id);
public Pay update(Pay pay);
public Optional<Pay> getById(Long id);
public List<Pay> getAll();
}
@Service
public class PayServiceImpl implements PayService {
@Resource
PayRepository payDao;
@Override
public Pay add(Pay pay){
return payDao.save(pay);
}
@Override
public int delete(Long id){
boolean exists = payDao.existsById(id);
if(exists){
payDao.deleteById(id);
return 1;
}else{
return 0;
}
}
@Override
public Pay update(PayDTO payDTO){
Optional<Pay> payOptional = getById(payDTO.getId());
if(payOptional.isEmpty()){
return null;
}else{
Pay pay = payOptional.get();
if(StringUtils.isNotBlank(payDTO.getPayNo())){
pay.setPayNo(payDTO.getPayNo());
}
if(StringUtils.isNotBlank(payDTO.getOrderNo())){
pay.setOrderNo(payDTO.getOrderNo());
}
if(payDTO.getUserId()!=null){
pay.setUserId(payDTO.getUserId());
}
if(payDTO.getAmount()!=null){
pay.setAmount(payDTO.getAmount());
}
return payDao.save(pay);
}
}
@Override
public Optional<Pay> getById(Long id){
return payDao.findById(id);
}
@Override
public List<Pay> getAll(){
return payDao.findAll();
}
}
创建包com.rocket.springcloud.provider.controller, 编写PayController:
@RestController
@Slf4j
public class PayController{
@Resource
PayService payService;
@DeleteMapping(value = "/pay/del/{id}")
public String deletePay(@PathVariable("id") Long id) {
int deleteCount = payService.delete(id);
return "成功删除记录:"+deleteCount;
}
@PutMapping(value = "/pay/update")
public String updatePay(@RequestBody PayDTO payDTO){
log.info(payDTO.toString());
payService.update(payDTO);
return "成功修改记录";
}
@GetMapping(value = "/pay/get/{id}")
public Pay getById(@PathVariable("id") Long id){
return payService.getById(id).get();
}
@GetMapping(value = "/pay/getAll")
public List<Pay> getAll(){
return payService.getAll();
}
}
6. 测试工程
测试一共有三种方式:使用IDEA自带的httpclient、Postman\Apifox、swagger3。
6.1 IDEA自带的httpclient测试接口
- 点击PayController上面接口地址,选择Generate request in Http client
- 修改调整请求参数,点击左边➡️ 执行,执行返回结果在下面json文件中查看
6.2 Postman测试
6.3 使用swagger3测试
swagger3基于注解进行文档维护,主要涉及Controller和实体类,常用的注解有下面这些:
注解 | 标识位置 | 作用 |
---|---|---|
@Tag | controller类 | 标识controller类的作用 |
@Parameter | 方法参数 | 标识参数的作用 |
@Parameters | 方法参数 | 参数的多重说明 |
@Schema | model层的JavaBean | 描述模型作用和每个模型 |
@Operation | 方法 | 描述方法作用 |
@ApiResponse | 方法 | 描述响应状态码等 |
修改PayController类, 创建配置swagger3类支持分组
@RestController
@Slf4j
@Tag(name="支付记录接口", description = "用于支付记录的增删改查接口")
public class PayController{
@Resource
PayService payService;
@PostMapping(value = "/pay/add")
@Operation(summary = "新增支付信息", description = "新增支付流水方法")
public String addPay(@RequestBody Pay pay){
log.info(pay.toString());
payService.add(pay);
return "成功插入记录";
}
@DeleteMapping(value = "/pay/del/{id}")
@Operation(summary = "删除支付信息", description = "删除支付流水方法")
public String deletePay(@PathVariable("id") Long id) {
int deleteCount = payService.delete(id);
return "成功删除记录:"+deleteCount;
}
@PutMapping(value = "/pay/update")
@Operation(summary = "更新支付信息", description = "更新单个支付流水方法")
public String updatePay(@RequestBody PayDTO payDTO){
log.info(payDTO.toString());
payService.update(payDTO);
return "成功修改记录";
}
@GetMapping(value = "/pay/get/{id}")
@Operation(summary = "查询单个支付信息", description = "查询单个支付流水方法")
public Pay getById(@PathVariable("id") Long id){
return payService.getById(id).get();
}
@GetMapping(value = "/pay/getAll")
@Operation(summary = "查询所有支付信息", description = "查询所有支付流水方法")
public List<Pay> getAll(){
return payService.getAll();
}
}
package com.rocket.springcloud.provider.config;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Swagger3Config {
@Bean
public GroupedOpenApi PayApi() {
return GroupedOpenApi.builder().group("支付微服务模块").pathsToMatch("/pay/**").build();
}
@Bean
public GroupedOpenApi OtherApi() {
return GroupedOpenApi.builder().group("其它微服务模块").pathsToMatch("/other/**", "/others").build();
}
@Bean
public OpenAPI docsOpenApi()
{
return new OpenAPI()
.info(new Info().title("cloud2024")
.description("通用设计rest")
.version("v1.0"))
.externalDocs(new ExternalDocumentation()
.description("www.jiebaba.cn")
.url("https://yiyan.baidu.com/"));
}
}
访问swagger文档首页查看接口文档生成内容:
7. 优化工程代码
7.1 时间格式问题
解决办法:
- 在实体类上对应的日期属性上面使用@JsonFormat注解
@ColumnDefault("CURRENT_TIMESTAMP")
@Column(name = "create_time", updatable = false)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Instant createTime;
@ColumnDefault("CURRENT_TIMESTAMP")
@Column(name = "update_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Instant updateTime;
- 可以在全局application.yml配置指定日期格式:
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
7.2 统一返回值
定义返回标准格式,其中有三个属性是固定的: code状态值、message描述、data数据,额外返回接口调用时间timestamp。其中code状态值可以参考Http的状态码, 设计为一个枚举值ReturnCodeEnum:
@Getter
public enum ReturnCodeEnum{
/**操作失败**/
RC999("999","操作XXX失败"),
/**操作成功**/
RC200("200","success"),
/**服务降级**/
RC201("201","服务开启降级保护,请稍后再试!"),
/**热点参数限流**/
RC202("202","热点参数限流,请稍后再试!"),
/**系统规则不满足**/
RC203("203","系统规则不满足要求,请稍后再试!"),
/**授权规则不通过**/
RC204("204","授权规则不通过,请稍后再试!"),
/**access_denied**/
RC403("403","无访问权限,请联系管理员授予权限"),
/**access_denied**/
RC401("401","匿名用户访问无权限资源时的异常"),
RC404("404","404页面找不到的异常"),
/**服务异常**/
RC500("500","系统异常,请稍后重试"),
RC375("375","数学运算异常,请稍后重试"),
INVALID_TOKEN("2001","访问令牌不合法"),
ACCESS_DENIED("2003","没有权限访问该资源"),
CLIENT_AUTHENTICATION_FAILED("1001","客户端认证失败"),
USERNAME_OR_PASSWORD_ERROR("1002","用户名或密码错误"),
BUSINESS_ERROR("1004","业务逻辑异常"),
UNSUPPORTED_GRANT_TYPE("1003", "不支持的认证模式");
/**自定义状态码**/
private final String code;
/**自定义描述**/
private final String message;
ReturnCodeEnum(String code, String message){
this.code = code;
this.message = message;
}
//遍历枚举
public static ReturnCodeEnum getReturnCodeEnum(String code){
return Arrays.stream(ReturnCodeEnum.values()).filter(x -> x.getCode().equalsIgnoreCase(code)).findFirst().orElse(null);
}
}
统一定义返回对象ResultData,并支持范型使得具有通用性:
@Data
@Accessors(chain = true)
public class ResultData<T> {
private String code;/** 结果状态 ,具体状态码参见枚举类ReturnCodeEnum.java*/
private String message;
private T data;
private long timestamp;
public ResultData (){
this.timestamp = System.currentTimeMillis();
}
public static <T> ResultData<T> success(T data) {
ResultData<T> resultData = new ResultData<>();
resultData.setCode(ReturnCodeEnum.RC200.getCode());
resultData.setMessage(ReturnCodeEnum.RC200.getMessage());
resultData.setData(data);
return resultData;
}
public static <T> ResultData<T> fail(String code, String message) {
ResultData<T> resultData = new ResultData<>();
resultData.setCode(code);
resultData.setMessage(message);
return resultData;
}
}
7.3 全局异常处理
编写GlobalExceptionHandler,捕获RuntimeException:
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler
{
/**
* 默认全局异常处理
* @param e 异常
* @return ResultData
*/
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResultData<String> exception(Exception e) {
System.out.println("----come in GlobalExceptionHandler");
log.error("全局异常信息exception:{}", e.getMessage(), e);
return ResultData.fail(ReturnCodeEnum.RC500.getCode(),e.getMessage());
}
}
7.4 调整PayController代码
使用ResultData作为统一返回数据:
@RestController
@Slf4j
public class PayController{
@Resource
PayService payService;
@Value("${server.port}")
String port;
@Value("${spring.application.name}")
String appName;
@PostMapping(value = "/pay/add")
public ResultData addPay(@RequestBody Pay pay){
log.info(pay.toString());
payService.add(pay);
return ResultData.success("成功插入记录");
}
@DeleteMapping(value = "/pay/del/{id}")
@Operation(summary = "删除支付信息", description = "删除支付流水方法")
public ResultData deletePay(@PathVariable("id") Long id) {
int deleteCount = payService.delete(id);
return ResultData.success("成功删除记录:"+deleteCount);
}
@PutMapping(value = "/pay/update")
@Operation(summary = "更新支付信息", description = "更新单个支付流水方法")
public ResultData<String> updatePay(@RequestBody PayDTO payDTO){
log.info(payDTO.toString());
payService.update(payDTO);
return ResultData.success("成功修改记录");
}
@GetMapping(value = "/pay/get/{id}")
@Operation(summary = "查询单个支付信息", description = "查询单个支付流水方法")
public ResultData getById(@PathVariable("id") Long id){
return payService.getById(id).map(
it->ResultData.success(it)).orElse(
ResultData.fail(ReturnCodeEnum.RC404.getCode(), "数据不存在"));
}
@GetMapping(value = "/pay/getAll")
@Operation(summary = "查询所有支付信息", description = "查询所有支付流水方法")
public
ResultData<List<Pay>> getAll(){
return ResultData.success(payService.getAll());
}
}
8. 创建cloud-consumer-order80工程
- 选择父工程springcloud-base,右键New->Module:
- 修改pom.xml
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--SpringBoot通用依赖模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 添加YML文件
在resource目录下添加application.yml:
server:
port: 80
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
application:
name: cloud-consumer-order
- 编写启动类OrderMain
@SpringBootApplication
public class OrderMain {
public static void main(String[] args) {
SpringApplication.run(OrderMain.class, args);
}
}
9. 远程调用RestTemplate
RestTemplate提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集。
9.1 常用API
- getForObject方法/getForEntity方法
返回对象为响应体中数据转化成的Json对象 - postForObject/postForEntity方法
返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等
9.2 配置RestTemplate
创建RestTemplateConfig配置类:
@Configuration
public class RestTemplateConfig{
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
9.3 服务远程调用
创建请求对象PayDTO, 订单调用接口类OrderController:
/**
* 一般而言,调用者不应该获悉服务提供者的entity资源并知道表结构关系,所以服务提供方给出的接口文档都都应成为DTO
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PayDTO implements Serializable {
private Integer id;
//支付流水号
private String payNo;
//订单流水号
private String orderNo;
//用户账号ID
private Integer userId;
//交易金额
private BigDecimal amount;
}
@RestController
public class OrderController{
public static final String PaymentSrv_URL = "http://localhost:8001";//先写死,硬编码
@Autowired
private RestTemplate restTemplate;
@PostMapping("/consumer/pay/add")
public ResultData addOrder(@ResponseBody PayDTO payDTO){
return restTemplate.postForObject(PaymentSrv_URL + "/pay/add",payDTO,ResultData.class);
}
@GetMapping("/consumer/pay/get/{id}")
public ResultData getPayInfo(@PathVariable("id") Integer id){
return restTemplate.getForObject(PaymentSrv_URL + "/pay/get/"+id, ResultData.class, id);
}
}
10. 工程重构
发现项目中有重复的模块代码, 可以提取到公共模块中:
10.1 创建工程cloud-api-commons
- 配置pom.xml
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--SpringBoot通用依赖模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
- 拷贝PayDTO和统一返回类到cloud-api-commons中,并将其他地方重复代码删除:
10.2 在其他工程添加cloud-api-commons依赖
<!-- 添加如下依赖 -->
<dependency>
<groupId>org.example</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
10.3 接口测试
使用apifox测试调用cloud-consumer-order80工程中接口: