1-springcloud-支付微服务准备

视频参考链接:https://www.bilibili.com/video/BV1gW421P7RD/

1.1 项目实战之 Pay 支付微服务

首先配合 boot 实现基本的下订单-支付的功能,在逐个加入微服务的组件。在项目实战中要严格遵守约定>配置>编码这一规定。

1.1.1 创建项目和 Maven 父工程

Maven 父工程只需要一个 pom.xml文件,创建步骤如下所示。

打开 IDEA 软件,选择New Project按钮,工程为 Maven ,JDK 版本为17。

字符编码设置为 UTF-8 ,选择左上角的"四条横线",选择设置(setting),接着在收缩框内输入"encoding",选择"File Encoding",将全部编码修改为UTF-8。

注解生效激活

Java 编译版本选17

File Type 过滤,该设置可操作也可不操作。

然后将pom.xml文件内容填充完整。

xml 复制代码
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.atguigu</groupId>
    <artifactId>cloud2024</artifactId>
    <version>1.0-SNAPSHOT</version>
    <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.30</lombok.version>
        <druid.version>1.1.20</druid.version>
        <mybatis.springboot.version>3.0.2</mybatis.springboot.version>
        <mysql.version>8.0.11</mysql.version>
        <swagger3.version>2.2.0</swagger3.version>
        <mapper.version>4.2.3</mapper.version>
        <fastjson2.version>2.0.40</fastjson2.version>
        <persistence-api.version>1.0.2</persistence-api.version>
        <spring.boot.test.version>3.1.5</spring.boot.test.version>
        <spring.boot.version>3.2.0</spring.boot.version>
        <spring.cloud.version>2023.0.0</spring.cloud.version>
        <spring.cloud.alibaba.version>2022.0.0.0-RC2</spring.cloud.alibaba.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!--springboot 3.2.0-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>${spring.boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--springcloud 2023.0.0-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--springcloud alibaba 2022.0.0.0-RC2-->
            <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集成mybatis-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.springboot.version}</version>
            </dependency>
            <!--Mysql数据库驱动8 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <!--SpringBoot集成druid连接池-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <!--通用Mapper4之tk.mybatis-->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper</artifactId>
                <version>${mapper.version}</version>
            </dependency>
            <!--persistence-->
            <dependency>
                <groupId>javax.persistence</groupId>
                <artifactId>persistence-api</artifactId>
                <version>${persistence-api.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.test.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

其中 dependencyManagement 与 dependency 的区别是,dependencyManagement 元素来提供了一种管理依赖版本号的方式。

通常会在一个组织或者项目的最顶层的父 POM 中看到 dependencyManagement 元素。

使用 pom.xml 中的 dependencyManagement 元素能让所有在子项目中引用一个依赖而不用显式的列出版本号。Maven 会沿着父子层次向上走,直到找到一个拥有 dependencyManagement 元素的项目,然后它就会使用这个 dependencyManagement 元素中指定的版本号。

这样做的好处就是:如果有多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号,优势:

1 这样当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要一个一个子项目的修改 ;
2 另外如果某个子项目需要另外的一个版本,只需要声明 version 就可。
  • dependencyManagement 里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。(即不下载依赖,只有外层为 dependence 才会下载依赖)

  • 如果不在子项目中声明依赖,是不会从父项目中继承下来的,只有在子项目中写了该依赖项并且没有指定具体版本,才会从父项目中继承该项且 version 和 scope 都读取自父 pom;

  • 如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。

1.1.2 Mapper 之一键生成

创建好项目后,即可开始 Mapper 的一键生成,本文采用 MyBatis 的 Mapper4,具体步骤如下所示。

相关链接网址:

  1. 在 SQL 软件 Navicat 中创建数据库名为 db2024 的数据库,然后创建表名为t_pay的表,具体 SQL 语句如下所示。

    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');
    
    SELECT * FROM t_pay;
  2. 创建普通 Maven 工程 mybatis-generater2024 ,具体操作如下。

    首先右击父工程 cloud2024 ,选择 New 选项下的 Module 模块,选择图片中的选项,最后单机 Create 按钮即可创建成功。

  3. 子模块 mybatis-generater2024 创建好后,开始完善子模块的 pom.xml 文件,文件具体内容如下所示。注意:plugin 和 dependceManagement 包裹的依赖不会自动下载,只有单独由 dependces 包裹的依赖才会自行下载。

    xml 复制代码
    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>com.atguigu</groupId>
            <artifactId>cloud2024</artifactId>
            <version>1.0-SNAPSHOT</version>
        </parent>
    
        <!--我自己独一份,只是一个普通Maven工程,与boot和cloud无关-->
        <artifactId>mybatis_generator2024</artifactId>
    
        <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>
            <!--Mybatis 通用mapper tk单独使用,自己独有+自带版本号-->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.13</version>
            </dependency>
            <!-- Mybatis Generator 自己独有+自带版本号-->
            <dependency>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-core</artifactId>
                <version>1.4.2</version>
            </dependency>
            <!--通用Mapper-->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper</artifactId>
            </dependency>
            <!--mysql8.0-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <!--persistence-->
            <dependency>
                <groupId>javax.persistence</groupId>
                <artifactId>persistence-api</artifactId>
            </dependency>
            <!--hutool-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
            </dependency>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <build>
            <resources>
                <resource>
                    <directory>${basedir}/src/main/java</directory>
                    <includes>
                        <include>**/*.xml</include>
                    </includes>
                </resource>
                <resource>
                    <directory>${basedir}/src/main/resources</directory>
                </resource>
            </resources>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.mybatis.generator</groupId>
                    <artifactId>mybatis-generator-maven-plugin</artifactId>
                    <version>1.4.2</version>
                    <configuration>
                        <configurationFile>${basedir}/src/main/resources/generatorConfig.xml</configurationFile>
                        <overwrite>true</overwrite>
                        <verbose>true</verbose>
                    </configuration>
                    <dependencies>
                        <dependency>
                            <groupId>mysql</groupId>
                            <artifactId>mysql-connector-java</artifactId>
                            <version>8.0.33</version>
                        </dependency>
                        <dependency>
                            <groupId>tk.mybatis</groupId>
                            <artifactId>mapper</artifactId>
                            <version>4.2.3</version>
                        </dependency>
                    </dependencies>
                </plugin>
            </plugins>
        </build>
    
    </project>
  4. 在目录 src/main/resources 目录下新建文件 config.properties 和 generatorConfig.xml 配置文件,其中 config.properties 文件用于配置 MySQL 相关信息,generatorConfig.xml 用于配置自动生成 Mapper 相关信息。具体配置信息如下所示。

    config.properties

    properties 复制代码
    #t_pay表包名
    package.name=com.atguigu.cloud
    
    # mysql8.0
    jdbc.driverClass = com.mysql.cj.jdbc.Driver
    jdbc.url= jdbc:mysql://localhost:3306/db2024?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
    jdbc.user = root
    jdbc.password = root

    generatorConfig.xml

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
            PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
            "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    
    <generatorConfiguration>
        <properties resource="config.properties"/>
    
        <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
            <property name="beginningDelimiter" value="`"/>
            <property name="endingDelimiter" value="`"/>
    
            <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
                <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
                <property name="caseSensitive" value="true"/>
            </plugin>
    
            <jdbcConnection driverClass="${jdbc.driverClass}"
                            connectionURL="${jdbc.url}"
                            userId="${jdbc.user}"
                            password="${jdbc.password}">
            </jdbcConnection>
    
            <javaModelGenerator targetPackage="${package.name}.entities" targetProject="src/main/java"/>
    
            <sqlMapGenerator targetPackage="${package.name}.mapper" targetProject="src/main/java"/>
    
            <javaClientGenerator targetPackage="${package.name}.mapper" targetProject="src/main/java" type="XMLMAPPER"/>
    
            <table tableName="t_pay" domainObjectName="Pay">
                <generatedKey column="id" sqlStatement="JDBC"/>
            </table>
        </context>
    </generatorConfiguration>
  5. 最后双击 Plugins 目录下的 mybatis-generator:generate 一键生成 entitle+mapper 接口和 xml 实现 SQL。具体步骤如下所示。

生成结果如下图所示。

1.1.3 Rest 通用 Base 工程构建

构建工程 cloud-provider-payment8001 微服务支付模块,实现支付功能。具体操作步骤如下所述。

1.1.3.1 支付项目构建过程
  1. 构建 module 模块,模块名为 cloud-provider-payment8001 ,首先右击 cloud2024 选择 module,输入如下图所示的信息即可创建微服务支付模块。并添加一个实体类 PayDTO。

    java 复制代码
    package com.atguigu.entities;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.io.Serializable;
    import java.math.BigDecimal;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class PayDTO implements Serializable {
        private Integer id;
        private String payNo;
        private String orderNo;
        private Integer orderId;
        private BigDecimal amount;
    }
  2. 改写 pom 文件,该文件主要内容如下。

    xml 复制代码
    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>com.atguigu</groupId>
            <artifactId>cloud2024</artifactId>
            <version>1.0-SNAPSHOT</version>
        </parent>
    
        <artifactId>cloud-provider-payment8001</artifactId>
        <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>
            <!--SpringBoot集成druid连接池-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
            </dependency>
            <!-- Swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html -->
            <dependency>
                <groupId>org.springdoc</groupId>
                <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            </dependency>
            <!--mybatis和springboot整合-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
            </dependency>
            <!--Mysql数据库驱动8 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.33</version>
            </dependency>
            <!--persistence-->
            <dependency>
                <groupId>javax.persistence</groupId>
                <artifactId>persistence-api</artifactId>
            </dependency>
            <!--通用Mapper4-->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper</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>
                <version>1.18.28</version>
                <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>
    
    </project>
  3. 在 src/main/resources 目录下创建 application.yml 文件,并配置信息。具体配置内容如下所示。

    yml 复制代码
    server:
      port: 8001
    
    # ==========applicationName + druid-mysql8 driver===================
    spring:
      application:
        name: cloud-payment-service
    
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/db2024?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
        username: root
        password: root
        druid:
          driver-class-name: com.mysql.cj.jdbc.Driver
    
    # ========================mybatis===================
    mybatis:
      mapper-locations: classpath:mapper/*.xml
      type-aliases-package: com.atguigu.entities
      configuration:
        map-underscore-to-camel-case: true
  4. 修改类 Main 为 Main8001,然后将其更改为应用类。具体代码如下所示。

    java 复制代码
    package com.atguigu;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import tk.mybatis.spring.annotation.MapperScan;
    
    @SpringBootApplication
    @MapperScan("com.atguigu.mapper")
    public class Main8001 {
        public static void main(String[] args) {
            SpringApplication.run(Main8001.class, args);
        }
    }
  5. 将业务类补充完整。具体操作如下。

    • 首先将生成的 Mapper 相关内容复制到同包位置,将 mapper 目录的 xml 文件复制到 resources 目录下的 mapper 文件中。具体操作如下所示。

    • 业务逻辑层 service ,主要包含接口 PayService 和 impl 包下的 实现类 PayServiceImpl。具体代码如下所示。

      PayService:

      java 复制代码
      package com.atguigu.service;
      
      import com.atguigu.entities.Pay;
      import com.atguigu.mapper.PayMapper;
      import com.atguigu.service.impl.PayServiceImpl;
      import jakarta.annotation.Resource;
      import org.springframework.stereotype.Service;
      
      import java.util.List;
      
      @Service
      public class PayService implements PayServiceImpl {
          @Resource
          private PayMapper  payMapper;
      
          @Override
          public int add(Pay pay) {
              return payMapper.insertSelective(pay);
          }
      
          @Override
          public int delete(Integer id) {
              return payMapper.deleteByPrimaryKey(id);
          }
      
          @Override
          public int update(Pay pay) {
              return payMapper.updateByPrimaryKeySelective(pay);
          }
      
          @Override
          public Pay getById(Integer id) {
              return payMapper.selectByPrimaryKey(id);
          }
      
          @Override
          public List<Pay> getAll() {
              return payMapper.selectAll();
          }
      }

      PayServiceImpl:

      java 复制代码
      package com.atguigu.service.impl;
      
      import com.atguigu.entities.Pay;
      
      import java.util.List;
      
      // 实现增删改查的接口
      public interface PayServiceImpl {
          public int add(Pay pay);
          public int delete(Integer id);
          public int update(Pay pay);
          public Pay getById(Integer id);
          public List<Pay> getAll();
      }
1.1.3.2 支付项目调试
1.1.3.2.1 PostMan 测试
  1. 添加记录测试

    通过输入以下代码添加支付记录,具体代码如下所示。

    json 复制代码
    {
        "payNo": "17204076",
        "orderNo": "6544de1c424a",
        "userId": "2",
        "amount": "19.90"
    }
  2. 删除记录测试

    通过输入路径参数 id,删除指定的记录。输入http://localhost:8001/pay/del/2并发送删除记录。

  3. 更新记录测试

  4. 通过属性 id 查询数据

  5. 查询全部数据

1.1.3.2.2 Swagger3 测试

前置条件,在 pom.xml 文件中导入如下依赖。

xml 复制代码
<!-- Swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html -->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
        </dependency>

知识点:详细情况见SpringCloud第3季2024.html-工程v1测试

1.1.3.3 优化项目
1.1.3.3.1 统一时间格式

统一时间格式有两种方案,第一种是通过注解@JsonFormat统一,第二种是通过 application.yml 配置文件统一,本节通过注解方式统一格式问题。

**方案1:**在数据访问层对应时间属性上添加注解@JsonFormate

java 复制代码
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")

**方案2:**在 SpringBoot 项目的配置文件 application.yml 中也可以配置格式

yml 复制代码
spring:
	jackson:
		date-format: yyyy-MM-dd HH:mm:ss
		timezone: GMT+8
1.1.3.3.2 统一返回值

定义返回标准格式有3大标配和一个扩展,具体内容如下所示。

  • code 状态值:由后端统一定义各种格式的状态码。

  • message 描述:错误状态码的描述信息。

  • data 数据:返回数据。

  • timestamp 时间戳:接口调用时间。

**新建枚举类 ReturnCodeEnum:**举值-构造-遍历

java 复制代码
package com.atguigu.resp;

import lombok.Getter;

import java.util.Arrays;

// 举值-构造-遍历
@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 getReturnCodeEnumV1(String code) {
        for (ReturnCodeEnum element:ReturnCodeEnum.values()) {
            if (element.getCode().equalsIgnoreCase(code)) {
                return element;
            }
        }
        return null;
    }
//    流处理
    public static ReturnCodeEnum getReturnCodeEnumV2(String code) {
        return Arrays.stream(ReturnCodeEnum.values())
                .filter(x -> x.getCode().equalsIgnoreCase(code))
                .findFirst().orElse(null);
    }
}

新建统一返回对象 ResultData:

java 复制代码
package com.atguigu.resp;

import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain=true) //链式写法
public class ResultData <T>{
    private String code;
    private String messsage;
    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.setMesssage(ReturnCodeEnum.RC200.getMessage());
        resultData.setData(data);
        return  resultData;
    }

    public static <T> ResultData<T> fail(String code,String messsage){
        ResultData<T> resultData = new ResultData<>();
        resultData.setCode(code);
        resultData.setMesssage(messsage);
        return  resultData;
    }
}

修改 PayController 控制类:

java 复制代码
package com.atguigu.controller;

import com.atguigu.entities.Pay;
import com.atguigu.entities.PayDTO;
import com.atguigu.resp.ResultData;
import com.atguigu.service.impl.PayServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/pay")
public class PayController {
    @Resource
    private PayServiceImpl payService;

    @PostMapping("/add")
    public ResultData<String> addPay(@RequestBody Pay pay) {
        System.out.println(pay.toString());
        int i = payService.add(pay);
        return ResultData.success("成功插入记录,返回:"+i);
    }

    @DeleteMapping("/del/{id}")
    public ResultData<Integer> deletePay(@PathVariable("id") Integer id) {
        return ResultData.success(payService.delete(id));
    }

    @PutMapping("/update")
    public ResultData<String> updatePay(@RequestBody PayDTO payDYO) {
        Pay pay = new Pay();
        BeanUtils.copyProperties(payDYO, pay);
        int i = payService.update(pay);
        return ResultData.success("成功修改记录,返回:"+i);
    }

    @GetMapping("/get/{id}")
    public ResultData<Pay> getPay(@PathVariable("id") Integer id) {
        return ResultData.success(payService.getById(id));
    }

    @GetMapping("/get")
    public ResultData<List<Pay>> getAll() {
        return ResultData.success(payService.getAll());
    }

}

**测试:**请参考1.1.3.2

1.1.3.3.3 全局异常处理

由于支付微服务没有异常处理,所以需要添加异常处理机制。异常处理机制有两种方式,分别是全局异常处理器、try-catch-finaly。

全局异常处理器类 GlobalExceptionHandler:

java 复制代码
package com.atguigu.exp;

import com.atguigu.resp.ResultData;
import com.atguigu.resp.ReturnCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultData<String> Exception(Exception e){
        System.out.println("-----come in GlobalExceptionHandler-----");
        log.error("全局异常信息:{}",e.getMessage(),e);
        return ResultData.fail(ReturnCodeEnum.RC500.getCode(), e.getMessage());
    }
}

异常处理PayController:

java 复制代码
package com.atguigu.controller;

import com.atguigu.entities.Pay;
import com.atguigu.entities.PayDTO;
import com.atguigu.resp.ResultData;
import com.atguigu.resp.ReturnCodeEnum;
import com.atguigu.service.impl.PayServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/pay")
public class PayController {
    @Resource
    private PayServiceImpl payService;

    @PostMapping("/add")
    public ResultData<String> addPay(@RequestBody Pay pay) {
        System.out.println(pay.toString());
        int i = payService.add(pay);
        return ResultData.success("成功插入记录,返回:"+i);
    }

    @DeleteMapping("/del/{id}")
    public ResultData<Integer> deletePay(@PathVariable("id") Integer id) {
        return ResultData.success(payService.delete(id));
    }

    @PutMapping("/update")
    public ResultData<String> updatePay(@RequestBody PayDTO payDYO) {
        Pay pay = new Pay();
        BeanUtils.copyProperties(payDYO, pay);
        int i = payService.update(pay);
        return ResultData.success("成功修改记录,返回:"+i);
    }

    @GetMapping("/get/{id}")
    public ResultData<Pay> getPay(@PathVariable("id") Integer id) {
        if(id==-4) throw new RuntimeException("id不能为负数");
        return ResultData.success(payService.getById(id));
    }

    @GetMapping("/get")
    public ResultData<List<Pay>> getAll() {
        return ResultData.success(payService.getAll());
    }

    @RequestMapping(value = "/error",method = RequestMethod.GET)
    public ResultData<Integer> error() {
        Integer i=Integer.valueOf(200);
        try {
            System.out.println("-----come here");
            int data=10/0;
        }catch (Exception e){
            e.printStackTrace();
            return ResultData.fail(ReturnCodeEnum.RC500.getCode(), e.getMessage());
        }
        return ResultData.success(i);
    }
}

整体目录结构如下所示。

1.1.4 订单微服务80 调用支付微服务8001

1.1.4.1 构建订单微服务80模块 cloud-consumer-order80
  1. 创建模块 cloud-consumer-order80

    首先右击 cloud2024 选择 Module 模块,输入如下属性即可创建订单微服务。

  2. 改 pom.xml 文件

    在 pom 文件输入以下内容。

    xml 复制代码
    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>com.atguigu</groupId>
            <artifactId>cloud2024</artifactId>
            <version>1.0-SNAPSHOT</version>
        </parent>
    
        <artifactId>cloud-consumer-order80</artifactId>
        <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>
            <!--web + actuator-->
            <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>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <!--hutool-all-->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
            </dependency>
            <!--fastjson2-->
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2</artifactId>
            </dependency>
            <!-- swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html -->
            <dependency>
                <groupId>org.springdoc</groupId>
                <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
  3. 写 yml 文件

    在 src/main/resourcs 目录下创建 application.yml 文件,并输入以下内容。

    yml 复制代码
    server:
      port: 80
  4. 主启动类设置

    将主启动类 Main 更改为Main80。

    java 复制代码
    package com.atguigu;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class Main80 {
        public static void main(String[] args) {
            SpringApplication.run(Main80.class, args);
        }
    }
  5. 业务类设置

    **entities:**构造PayDTO类

    java 复制代码
    package com.atguigu.entities;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.io.Serializable;
    import java.math.BigDecimal;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class PayDTO implements Serializable {
        private Integer id;
        private String payNo;
        private String orderNo;
        private Integer userId;
        private BigDecimal amount;
    }

    **ResulData:**直接将支付微服务部分粘贴过来,本节将不在写代码。

    RestTemplate:

    RestTemplate提供了多种便捷访问远程Http服务的方法, 是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集。RestTemplate官网地址

    RestTemplate常用API 使用说明:

    使用restTemplate访问restful接口非常的简单粗暴无脑。(url, requestMap, ResponseBean.class)这三个参数分别代表 REST 请求地址、请求参数、HTTP响应转换被转换成的对象类型。

    GET 请求方法:用于查询数据,参数("支付微服务url",返回值类型.class,传递参数,如无传递参数最后一个参数才可不写)

    txt 复制代码
    <T> T getForObject(String url, Class<T> responseType, Object... uriVariables);
    <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables);
    <T> T getForObject(URI url, Class<T> responseType);
    <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables);
    <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables);
    <T> ResponseEntity<T> getForEntity(URI var1, Class<T> responseType);

    POST 请求方法:用于更新、删除、添加数据,参数("支付微服务url",传递参数,返回值类型.class)

    txt 复制代码
    <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);
    <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);
    <T> T postForObject(URI url, @Nullable Object request, Class<T> responseType);
    <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables);
    <T> ResponseEntity<T> postForEntity(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables);
    <T> ResponseEntity<T> postForEntity(URI url, @Nullable Object request, Class<T> responseType);

    **config 配置类:**在 config 目录下创建配置类 RestTemplateConfig

    java 复制代码
    package com.atguigu.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.client.RestTemplate;
    
    @Configuration
    public class RestTemplateConfig {
        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }

    controller 控制层:

    java 复制代码
    package com.atguigu.controller;
    
    import com.atguigu.entities.PayDTO;
    import com.atguigu.resp.ResultData;
    import jakarta.annotation.Resource;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    @RequestMapping("/consumer/pay")
    public class OrderController {
        public static final String PAYMENTSRV_URL = "http://localhost:8001";
    
        @Resource
        private RestTemplate restTemplate;
        
        @GetMapping("/add")
        public ResultData addOrder(PayDTO  payDTO){
            return restTemplate.postForObject(PAYMENTSRV_URL+"/pay/add",payDTO,ResultData.class);
        }
        @GetMapping("/del/{id}")
        public ResultData deletePayInfo(@PathVariable("id") Integer id){
            return restTemplate.postForObject(PAYMENTSRV_URL+"/del/"+id,id,ResultData.class);
        }
        @GetMapping("/update/")
        public ResultData updatePayInfo(PayDTO  payDTO){
            return restTemplate.postForObject(PAYMENTSRV_URL+"/update/",payDTO,ResultData.class);
        }
        @GetMapping("/get/{id}")
        public ResultData getPayInfo(@PathVariable("id") Integer id){
            return restTemplate.getForObject(PAYMENTSRV_URL+"/get/"+id,ResultData.class,id);
        }
        @GetMapping("/get/")
        public ResultData getPayAllInfo(){
            return restTemplate.getForObject(PAYMENTSRV_URL+"/get/",ResultData.class);
        }
    }
  6. 测试

首先启动订单微服务(Main80.class)和支付微服务(Main8001.class),然后点击"Service"图标选择 "Add Service"中的SpringBoot 将两个微服务放在一起。具体操作如下所示。

操作完成之后即可打开 Postman 测试工具进行测试。

请输入http://localhost/consumer/pay/get/1http://localhost/consumer/pay/del/5http://localhost/consumer/pay/update?id=1&payNo=1314&orderNo=1213&userId=2&account=3.33http://localhost/consumer/pay/add?payNo=1213&orderNo=1213&userId=2&account=3.33 进行查询、删除、更新和添加测试。

1.1.4.2 优化项目

这部分是工程重构的部分,有部分功能是共用的,为避免重复编写代码将其封装到一个通用的模块中,并进行 install安装成 jar 包,并在需要的模块中引入坐标。

  1. 新建通用模块 cloud-api-commons

  2. 改写 pom.xml 文件

    xml 复制代码
    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>com.atguigu</groupId>
            <artifactId>cloud2024</artifactId>
            <version>1.0-SNAPSHOT</version>
        </parent>
    
        <artifactId>cloud-api-commons</artifactId>
        <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>
    
    </project>
  3. 引入通用部分内容和全局异常处理器

  4. 安装通用工具包,需要先 clean 在 install。由于父工程的lombok 版本1.8.26太低,需要升级版本到1.8.30版本及以上。

  5. 删除订单80和支付8001中重复部分的内容,并引入步骤4打包的工具包的坐标

    xml 复制代码
    <!--自定义依赖-->
            <dependency>
                <groupId>com.atguigu</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>

    只保留如下图所示的图片。

  6. 测试

测试与1.1.4.1测试一致,此处不在赘述。

1.1.5 项目实战存在的问题

微服务所在的IP地址和端口号硬编码到订单微服务中,会存在非常多的问题

(1)如果订单微服务和支付微服务的IP地址或者端口号发生了变化,则支付微服务将变得不可用,需要同步修改订单微服务中调用支付微服务的IP地址和端口号。

(2)如果系统中提供了多个订单微服务和支付微服务,则无法实现微服务的负载均衡功能。

(3)如果系统需要支持更高的并发,需要部署更多的订单微服务和支付微服务,硬编码订单微服务则后续的维护会变得异常复杂。

所以,在微服务开发的过程中,需要引入服务治理功能,实现微服务之间的动态注册与发现,从此刻开始我们正式进入SpringCloud实战

下一页 2-SpringCloud-Consul服务注册与发现和分布式配置管理

相关推荐
这是一个懒人7 小时前
mac maven 安装
java·macos·maven
自由的疯7 小时前
Java Kubernetes本地部署
java·后端·架构
自由的疯7 小时前
Java Kubernetes本地部署RuoYi框架jar包
java·后端·架构
Query*7 小时前
Java 设计模式—— 责任链模式:从原理到 SpringBoot 最优实现
java·spring boot·责任链模式
Query*7 小时前
Java 设计模式——适配器模式:从原理到3种实战的完整指南
java·设计模式·适配器模式
Meteors.7 小时前
23种设计模式——状态模式(State Pattern)
java·设计模式·状态模式
yaoxin52112310 小时前
211. Java 异常 - Java 异常机制总结
java·开发语言·python
Predestination王瀞潞15 小时前
Java EE开发技术(Servlet整合JDBC银行管理系统-上)
java·servlet·java-ee·jdbc
寻星探路15 小时前
Java EE初阶启程记13---JUC(java.util.concurrent) 的常见类
java·开发语言·java-ee