Skip to content

基于老SpringCloud工程

1. 业务需求

Alt text

2. 新建父工程

  1. 打开IDEA, 点击File->New->Project:
    Alt text
  2. 配置注解生效 Alt text
  3. 配置pom.xml
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子工程

  1. 选择父工程springcloud-base,右键New->Module:
    Alt text
  2. 修改pom.xml
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>
  1. 添加YML文件
    在resource目录下添加application.yml:
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
  1. 添加启动类
java
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插件

Alt text

4.2 执行数据库初始化脚本

使用DBeaver客户端工具连接MySQL数据库,执行以下脚本:

sql
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:
Alt text 进入可视化表实体面板,里面可以选择表和编辑实体名称Pay:
Alt text 点击完成后生成Pay对象, 在Pay的类上加上@DynamicUpdate注解,更新的时候Hibernate会比较entity实体类中发生变化的字段值,只更新那些字段值发生变化的列。

java
@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字段,而是更新变化的字段, 解决办法:

  1. 自己写一个方法先findOne查出原有值再把不想变化的字段塞回要save()的对象中。
  2. 是使用SQL语句。 创建包com.rocket.springcloud.provider.dto, 右键New->选择DTO: Alt text 选择需要的属性和编辑类名,支持配置常见的校验,设置类名为PayDto:
    Alt text 生成的PayDto代码如下:
java
/**
 * 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@Getter,被@Value标识的类不可被修改,不能与@RequestBody`、jackson同时使用。
创建包com.rocket.springcloud.provider.dao, 右键New->选择SpringData JPA Respository: Alt text 选择我们要操作的Entity对象,设置类名为PayRespository:
Alt text 点击完成生成, 添加@Repository注解使得加入Spring容器:

java
@Repository
public interface PayRepository extends JpaRepository<Pay, Long> {

}

5. 编写service\controller代码

创建包com.rocket.springcloud.provider.service, 编写PayService,PayServiceImpl:

java
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();
}
java
@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:

java
@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测试接口

  1. 点击PayController上面接口地址,选择Generate request in Http client Alt text
  2. 修改调整请求参数,点击左边➡️ 执行,执行返回结果在下面json文件中查看

6.2 Postman测试

Alt text

6.3 使用swagger3测试

swagger3基于注解进行文档维护,主要涉及Controller和实体类,常用的注解有下面这些:

注解标识位置作用
@Tagcontroller类标识controller类的作用
@Parameter方法参数标识参数的作用
@Parameters方法参数参数的多重说明
@Schemamodel层的JavaBean描述模型作用和每个模型
@Operation方法描述方法作用
@ApiResponse方法描述响应状态码等

修改PayController类, 创建配置swagger3类支持分组

java
@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();
    }
}
java
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文档首页查看接口文档生成内容:
Alt text

7. 优化工程代码

7.1 时间格式问题

Alt text 解决办法:

  1. 在实体类上对应的日期属性上面使用@JsonFormat注解
java
    @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;
  1. 可以在全局application.yml配置指定日期格式:
yml
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

7.2 统一返回值

定义返回标准格式,其中有三个属性是固定的: code状态值、message描述、data数据,额外返回接口调用时间timestamp。其中code状态值可以参考Http的状态码, 设计为一个枚举值ReturnCodeEnum:

java
@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,并支持范型使得具有通用性:

sh
@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:

java
@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作为统一返回数据:

java
@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工程

  1. 选择父工程springcloud-base,右键New->Module:
    Alt text
  2. 修改pom.xml
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>
  1. 添加YML文件
    在resource目录下添加application.yml:
yml
server:
  port: 80

spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  application:
    name: cloud-consumer-order
  1. 编写启动类OrderMain
java
@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

  1. getForObject方法/getForEntity方法
    返回对象为响应体中数据转化成的Json对象
  2. postForObject/postForEntity方法
    返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等

9.2 配置RestTemplate

创建RestTemplateConfig配置类:

java
@Configuration
public class RestTemplateConfig{
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

9.3 服务远程调用

创建请求对象PayDTO, 订单调用接口类OrderController:

java
/**
 * 一般而言,调用者不应该获悉服务提供者的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;
}
java
@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. 工程重构

发现项目中有重复的模块代码, 可以提取到公共模块中:
Alt text

10.1 创建工程cloud-api-commons

  1. 配置pom.xml
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>
  1. 拷贝PayDTO和统一返回类到cloud-api-commons中,并将其他地方重复代码删除:
    Alt text

10.2 在其他工程添加cloud-api-commons依赖

xml
<!-- 添加如下依赖 -->
<dependency>
    <groupId>org.example</groupId>
    <artifactId>cloud-api-commons</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

10.3 接口测试

使用apifox测试调用cloud-consumer-order80工程中接口:
Alt text