2. Spring Cloud 微服务基础环境搭建

187 阅读12分钟

2. Spring Cloud 微服务基础环境搭建

@[toc]

前言

1. 微服务需求解析

在这里插入图片描述

说明:

我们通过浏览器输入: http://localhost//member/consumer/get/1 这样一个服务消费微服务模块 ,这个服务消费微服务模块,底层访问——> 服务提供 消费微服务模块(当中所提供的接口/方法)进行对数据库的查询和添加数据的操作。

简单的说就是两个模块,处理业务

  • member-service-consumer-80 : 作为服务消费微服务模块,提供给客户端显示处理,连接浏览器

  • member-service-provider-10000 : 作为服务提供 消费微服务模块,处理前端实际提交的业务,进行处理

  • 特别说明:这里每个模块,用于区分用的是那个端口,我们在模块名后面-追加一个 端口信息 。如下:

    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

特别说明: 这里我们对于**每个模块的 Spring Boot 的场景启动器上的类后面追加上一个“该模块所占用的端口号” ** ,方便我们查找该明确所占用的端口信息。如下图所示:

在这里插入图片描述

2. 具体搭建微服务步骤:

对于一个微服务 的搭建,这里我们采用父工程,利用 Maven版本仲裁 简化配置。

所以,我们就需要创建一个父项目,通过父项目管理我们的多个微服务的各个所提供的功能的模块。

2.1 创建父工程 ,用于聚合其它微服务模块

2.1.1 需求说明/图解

在这里插入图片描述

2.1.2 具体实现步骤

  1. 我们需要先创建一个父项目/父工程 ,该父工程会去管理我们父工程下的多个微服务模块(module) ,如下图所示:( 该图是我们学习微服务内容的所有内容上该微服务配置好的内容 )

在这里插入图片描述

  1. 创建父项目步骤:- 灵活配置方式。

在这里插入图片描述

在这里插入图片描述

特别的: 当我们创建一个项目成功后,要修改对应该项目所选配的配置。比如:

  1. 指明该项目我们所要运行的 Java 的版本,这里我们选择 Java 8 的版本

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 配置我们项目的字符集的编码,这里我们选择配置 utf-8 的编码

在这里插入图片描述

  1. 配置指明我们本地的 maven 工具,不要用 idea 所提供的的 maven 工具

在这里插入图片描述

这里我们将父项目作为一个简单纯净的环境(因为我们的这个微服务项目,这个父模块部分,不需要编写代码,仅仅作为一个管理多个模块的管理器) ,所以我们可以将其 idea 自动为我们生成 src 目录删除。作为一个简单的纯净的开发环境。

在这里插入图片描述

配置父工程 pom.xml, 作为聚合其它模块

我们在父工程 e-commerce-center 当中的 pom.xml 文件当中配置相关所需要的 jar的依赖。

如下:相关的依赖。

在这里插入图片描述

在 <propertis> 标签当中指明:相关我们所需要的所需的依赖的 jar 包当中版本的内容。进行一个版本仲裁

在这里插入图片描述

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <!--        在父项目当中:定义版本仲裁-->
        <junit.version>4.12</junit.version>
        <!--    说明: 使用最新版本 log4j ,防止安全漏洞-->
        <log4j.version>2.17.2</log4j.version>
        <lombok.version>1.18.20</lombok.version>
        <!--    老师的 5.1.47  本电脑安装的是   8.0.26-->
        <mysql.version>8.0.26</mysql.version>
        <druid.version>1.1.17</druid.version>
        <mybatis.spring.boot.version>2.2.0</mybatis.spring.boot.version>
    </properties>

同时我们需要在: <dependencyManagement> 配置各个依赖和版本

当我们在父项目/父工程当中配置了该 <dependencyManagement\> 的标签内容,需要注意的一点是: 这里的依赖的 jar 并没有引入到项目当中,仅仅这是一个声明而已 <dependencyManagement> 里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要的添加依赖相对应的依赖信息。

在这里插入图片描述

<!--  <dependencyManagement> 配置各个依赖和版本
       子模块继承后,锁定版本,子模块不用再写 version 了
       需要注意的一点是: 这里的依赖的 jar 并没有引入到项目当中,仅仅这是一个声明而已
        <dependencyManagement> 里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要的依赖 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <!--
                 1.type:pom 和 scope: import 配合使用
                 2.表示父项目的子模块,在引入 springboot 相关依赖时,锁定版本为 2.2.2.RELEASE
                 3.通过 pom + import 解决maven单继承机制
                 4. 子模块之间可以多继承-->
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--            配置 Spring Cloud-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--            配置 Spring Cloud Alibaba-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--            mysql 数据库-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <!--             ${mysql.version} 锁定引用上面<mysql.version>标签配置的版本信息 -->
                <version>${mysql.version}</version>
            </dependency>

            <!--            druid 数据库连接池-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>

            <!--            spring boot -->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.version}</version>
            </dependency>

            <!--       log4j 日志     -->
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>

            <!--            junit 配置-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>


            <!--            lombok 配置-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.20</version>
            </dependency>

        </dependencies>
    </dependencyManagement>

特别说明: type: pom 和 scope: import 配置使用 的作用:让多个模块之间可以多继承,解决 maven 单继承机制。

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.2.RELEASE</version>
                <!--
                 1.type:pom 和 scope: import 配合使用
                 2.表示父项目的子模块,在引入 springboot 相关依赖时,锁定版本为 2.2.2.RELEASE
                 3.通过 pom + import 解决maven单继承机制
                 4. 子模块之间可以多继承-->
                <type>pom</type>
                <scope>import</scope>
            </dependency>

删除不需要的配置节点。

这里我们删除 buildreporting 节点下的内容信息即可。

2.1.3 注意事项和具体细节

dependencyManagement 细节说明

  1. Maven 使用 <dependencyManagement> 元素来提供了一种管理依赖版本号的方式, 通常在项目 packaging为POM,中使用 <dependencyManagement> 元素
  2. 使用 pom.xml 中的 <dependencyManagement> 标签元素能让所有在子项目中引用一个依赖, Maven 会沿着父子层次向上走,直到找到一个拥有 <dependencyManagement> 标签元素的项目, 然后它就会使用这个 <dependencyManagement> 元素中指定的版本号。 3.好处: 如果有多个项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号, 当升级或切换到另一个版本时,只需要在顶层父容器更新,而不需要分别在子项目的修改;另外 如果某个子项目需要另外一个版本,只需要声明 version 就可以。
  3. <dependencyManagement> 里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要的依赖
  4. 如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且 versionscope 都读取自父 pom 所配置的 <dependencyManagement> 的内容。

在这里插入图片描述

在这里插入图片描述

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

2.2 创建会员中心微服务模块 -service provider

2.2.1 需求说明/图解

通过浏览器可以获取会员信息(通过会员中心微服务模块)

在这里插入图片描述

同样的使用 Postman 工具可以进行一个访问操作的问题。

在这里插入图片描述

通过 Postman 进行一个添加用户的操作。如下图所示:

在这里插入图片描述

准备工作:我们创建对应所需的数据库和数据表的内容

如下:创建数据库,创建数据表,添加初始表当中的内容的 SQL 所执行的脚本

这里我们创建的e_commerce_center_db 的数据库的内容目前仅仅只是添加了一张数据表 member 员工的数据表。

CREATE DATABASE e_commerce_center_db;
USE e_commerce_center_db;
CREATE TABLE member
(
    `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'id',
    `NAME` VARCHAR(64) COMMENT '用户名',
    pwd CHAR(32) COMMENT '密码',
    mobile VARCHAR(20) COMMENT '手机号码',
    email VARCHAR(64) COMMENT '邮箱',
    gender TINYINT COMMENT '性别',
    PRIMARY KEY (id)
);

INSERT INTO member VALUES
(NULL, 'smith', MD5('123'), '123456789000', 'smith@sohu.com', 1);
SELECT * FROM member

特别说明:为了方便后续的记忆操作,这里我们用户的密码都配置为了 123 ,因为我们对用户的密码进行了一个 MD5 的加密处理,所以我们无法明文的看到对应设置的密码。

在这里插入图片描述

2.2.2 具体实现步骤:

这里:我们创建 member-service-provider-10000 微服务模块[提供会员服务] 。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

member-service-provider-10000 模块当中的 pom.xml 文件当中引入相关的 jar 的依赖。如下图所示:

在这里插入图片描述

详细配置如下:

<?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">
    <!--    这是对应该子模块 module 上的父模块-->
    <parent>
        <artifactId>e-commerce-center</artifactId>
        <groupId>com.rainbowsea</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <!--    该项目继承父项目的groupId 是 com.rainbowsea.sprngcloud-
            因此这里就不需要再指定 groupid 版本了 -->
    <artifactId>member-service-provider-10000</artifactId>

    <!--    引入相关的依赖-->
    <dependencies>
        <!--        引入web-starter 说明:这里我们使用版本仲裁(从父项目继承了版本)-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--1. starter-actuator 是sprng boot 程序的监控系统,可以实现健康检查,info 信息等
            2. 访问http://localhost:10000/actuator 可以看到相关链接,还可以做相关配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--        引入 mybatis-starter 整合到 springboot 当中-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>


        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <!--  这里我们重新指定一下 version 版本,因为父项目当中没有-->
            <version>1.1.13</version>
        </dependency>

        <!--        引入 mysql依赖,使用版本仲裁-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--      spring-boot-starter-jdbc 引入  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!--        lombok 引入-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>


        <!--        引入 test-starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--        引入我们自己对 bean 封装成 api 的模块内容-->
        <dependency>
            <groupId>com.rainbowsea</groupId>
            <artifactId>e_commerce_center-common-api</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!-- 引入 eureka-client 依赖  -->
        <!--        注意:存在一个 starter 不要选错了-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

    </dependencies>


</project>

在这里插入图片描述

同时需要注意:所加载的 jar 的内容的版本是否与你在父项目/模块当中指定的版本一致;

在这里插入图片描述

在 在 member-service-provider-10000 模块当中的resources 目录下创建一个:application.yaml 文件,后缀为 yml 也是一样的,但是注意:文件名必须是 application 不可以是其它的。

在这里插入图片描述

在该 application.yaml 文件当中编写,相关的配置信息,比如: mybatis,springboot,springcloud 等等配置信息。如下图所示:

在这里插入图片描述

server:
  port: 10000
spring:
  application:
    name: member-service-provider-10000 # 配置应用的名称
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource # 配置 alibaba 的数据库连接池
    password: MySQL123
    username: root
    url: jdbc:mysql://localhost:3306/e_commerce_center_db?useSSL=true&useUnicode=true&characterEncoding=UTF-8
mybatis:
  mapper-locations: classpath:mapper/*.xml # 指定 mapper.xml 文件位置 classpath 表示 resources 目录下
  type-aliases-package: com.rainbowsea.springcloud.entity # 实例 bean 类所在的包,这样可以通过类名的方式

我们在 member-service-provider-10000 模块当中创建一个 com.rainbowsea.springcloud 下创建一个场景启动类。

在这里插入图片描述

package com.rainbowsea.springcloud;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;


////@EnableEurekaClient 表示将该程序/项目/模块 标识为 eureka-client 端
//@EnableEurekaClient
@SpringBootApplication
public class MemberApplication10000 {

    public static void main(String[] args) {
        SpringApplication.run(MemberApplication10000.class, args);
    }
}

在该项目当中的创建一个包为 com.rainbowsea.springcloud.entity 下创建,我们所需要的 Java Bean 的类。两个:Result,以及 Member 两个 java 类。对于 Member 类我们使用 Lombak 插件进行自动生成,而这里的 Result 所需要的方法,需要自定义静态方法,使用 Lombak 自动生成的方法,不够使用,所以 Result 就自己手动编写了。

在这里插入图片描述

package com.rainbowsea.springcloud.entity;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Member implements Serializable { // Serializable 表示传输时的序列化操作
    private Long id;
    private String name;
    private String pwd;
    private String mobile;
    private String email;
    private Integer gender;
}

Result 该类用于,前后端交互的一个返回结果,同时利于 json 格式的内容上的转换。这个工具类,我们可以在网上很轻松的找到

特别的该 Result 类当中有: 三个成员变量:

  • private String code; // 表示后端提交给前端所显示的 :状态码

  • private String msg; // 表示对后端提交的状态码的一个说明解释的内容,比如 404 提示找不到内容的提示 等等

  • private T data; // 表示对后端返回给前端的数据/携带的数据, 为了扩展性好,我们这里使用泛型,任何数据类型基本上都可以满足。

public class Result<T> implements Serializable {


    private String code; //状态码
    private String msg; //对状态说明
    private T data; // 返回时,携带的数据, 为了扩展性好,老师使用泛型
}

在这里插入图片描述

package com.rainbowsea.springcloud.entity;


import java.io.Serializable;

/**
 * 1. 用于返回结果, 利于 json 格式
 * 2. 这个工具类, 在网上也可找到
 */
public class Result<T> implements Serializable {


    private String code; //状态码
    private String msg; //对状态说明
    private T data; // 返回时,携带的数据, 为了扩展性好,老师使用泛型

    //无参构造器
    public Result() {

    }

    //带参构造器-指定返回的data
    public Result(T data) {
        this.data = data;
    }

    //编写方法-返回需要的Result对象-表示成功的Result
    public static Result success() {
        Result result = new Result<>();
        result.setCode("200");
        result.setMsg("success");
        return result;
    }

    //编写方法-返回需要的Result对象-表示成功的Result,同时可以携带数据
    //如果需要在static方法使用泛型,需要在 static <T>
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>(data);
        result.setCode("200");
        result.setMsg("success");
        return result;
    }

    //编写方法-返回需要的Result对象-表示成功的Result,同时可以携带数据和指定msg
    //如果需要在static方法使用泛型,需要在 static <T>
    public static <T> Result<T> success(String msg, T data) {
        Result<T> result = new Result<>(data);
        result.setCode("200");
        result.setMsg(msg);
        return result;
    }

    //编写方法-返回需要的Result对象-表示失败的Result
    public static Result error(String code, String msg) {
        Result result = new Result<>();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }

    public static <T> Result<T> error(String code, String msg, T data) {
        Result<T> result = new Result<>(data);
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    //编写方法-返回需要的Result对象-表示失败的Result,同时可以携带数据

    public void setData(T data) {
        this.data = data;
    }
}




Result 类当中的方法说明:

如何在静态方法中使用泛型

我们知道在Java当中 泛型 是在 实例化对象 才确定的,而静态方法 则是,加载类的时候就确定了的,两个内容的加载时期对应不上,所以:直接在静态方法当中使用 泛型是会报错的。如下:

在这里插入图片描述

解决方法就是:我们只需要在静态方法中的 static 关键字的后面加上 <T> 其泛型即可。如下:public static <T> Result<T> error(String code, String msg, T data)

在这里插入图片描述

在该项目中member-service-provider-10000 模块当 创建一个 com.rainbowsea.springcloud.dao 该包下创建 mybatis 所执行SQL语句的方法/接口,如下:

注意:添加上 @Mapper 注解,让被 Spring IOC 容器管理起来。如果存在多个 需要添加 @Mapper 注解的类,可以在对应的该项目当中的场景启动项目 当中添加 @MapperScan(指明要扫描的路径)

这里我们目前就添加两个接口方法

  • Member queryMemberById(Long id); // 根据 id 返回 member 数据
  • int save(Member member); // 添加 member 数据到数据库,数据表当中

在这里插入图片描述

package com.rainbowsea.springcloud.dao;

import com.rainbowsea.springcloud.entity.Member;
import org.apache.ibatis.annotations.Mapper;


@Mapper  // 标注注解被扫描到,或是在 配置类/场景启动项中 @MapperScan(指明扫描路径)
public interface MemberDao {

    // 定义方法
    // 根据 id 返回 member 数据
    Member queryMemberById(Long id);

    /**
     * 添加 member
     *
     * @param member
     * @return
     */
    int save(Member member);
}

编写上述我们在 com.rainbowsea.springcloud.dao 包下所编写的执行SQL的接口方法,所对应的 SQL 脚本内容信息。如下:

在这里插入图片描述

如下我们编写相关的所需要执行的 SQL 语句的脚本信息:

在这里插入图片描述

在这里插入图片描述

特别的:这里我们启动的了 Mybatis 的别名机制,在该 member-service-provider-10000 模块当中我们所配置的 application.yaml 的内容。

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?> <!-- 这句不要动,表示xml的版本,以及读取的编码 -->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rainbowsea.springcloud.dao.MemberDao">
    <!--    配置实现 queryMemberById-->
    <!--
        1.这里可以使用 resultType="Member"
        2.当然也可以使用 resultMap="自定义的resultMap",这里我们使用resultMap
    -->

    <resultMap id="BaseResultMap" type="com.rainbowsea.springcloud.entity.Member">
        <id column="id" property="id" jdbcType="BIGINT"></id>
        <id column="name" property="name" jdbcType="VARCHAR"></id>
        <id column="pwd" property="pwd" jdbcType="VARCHAR"></id>
        <id column="mobile" property="mobile" jdbcType="VARCHAR"></id>
        <id column="email" property="email" jdbcType="VARCHAR"></id>
        <id column="gender" property="gender" jdbcType="TINYINT"></id>
    </resultMap>
    <!--实现查询-->
    <select id="queryMemberById" parameterType="Long" resultMap="BaseResultMap">
        select * from `member` where `id`=#{id}
    </select>
    <!--    实际开发中,我们使用的是 mybatis,还是 mybatis-plus 两者都要会-->
    <!--  useGeneratedKeys="true" keyProperty="id" 表示如果插入的表id 以自证列为主键,那么允许jdbc自动生成主键,并可将
      自动生成的主键id返回,注意:useGeneratedKeys="true" 只针对 insert 语句生效, 默认为 false-->
    <insert id="save" parameterType="member" useGeneratedKeys="true" keyProperty="id">
        insert into member(`NAME`,`pwd`,`mobile`,`email`,`gender`)
        values(#{name}, md5(#{pwd}), #{mobile},#{email}, #{gender});
    </insert>
</mapper>

编写相关的 service 层的内容信息。

在这里插入图片描述

编写 memberDao 和 Service 的内容的测试类,进行一个测试。

需要注意的是:如果要测试,该项目必须要有场景启动器,同时场景启动器添加上了 @SpringBootApplication 注解。才行。

@SpringBootTest // 如果指明的路径不同;要加上注解指明(场景启动的所在包路径)// 同时一定要有场景启动器注解
// 如果不想把测试类放到和启动类相同的包下,那就给测试类的注解加上@SpringBootTest(classes = {springbootJpaApplication.class}) 代

在这里插入图片描述

在这里插入图片描述

package com.rainbowsea.springcloud;


import com.rainbowsea.springcloud.dao.MemberDao;
import com.rainbowsea.springcloud.entity.Member;
import com.rainbowsea.springcloud.service.MemberService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;

@SpringBootTest // 如果指明的路径不同;要加上注解指明(场景启动的所在包路径)// 同时一定要有场景启动器注解
// 如果不想把测试类放到和启动类相同的包下,那就给测试类的注解加上@SpringBootTest(classes = {springbootJpaApplication.class}) 代
@Slf4j
public class MemberApplicationTest {

    @Resource
    private MemberDao memberDao;

    @Resource
    private MemberService memberService;

    @Test  // 注意选择: org.junit.jupiter.api.Test; 包当中的
    // 注意方法不能定义为 private 私有的,不然无法测试运行的
    public void testQueryMemberById() {
        Member member = memberDao.queryMemberById(1L);
        log.info("member={}", member);
    }


    @Test
    public void testMemberDaosave() {
        Member member = new Member(null, "牛魔王", "123", "1300000", "nmw@shou.com", 1);
        int affected = memberDao.save(member);
        log.info("affected={}", affected);
    }


    @Test  // 注意选择: org.junit.jupiter.api.Test; 包当中的
    // 注意方法不能定义为 private 私有的,不然无法测试运行的
    public void testMemberServiceQueryMemberById2() {
        Member member = memberService.queryMemberById(1L);
        log.info("member={}", member);
    }


    @Test
    public void testMemberServiceSave() {
        Member member = new Member(null, "狐狸精", "123", "1300000", "hlj@shou.com", 2);
        int affected = memberService.save(member);
        log.info("affected={}", affected);
    }
}

编写相关的 controller 控制器,进行业务上的处理。

在这里插入图片描述

在这里插入图片描述

package com.rainbowsea.springcloud.controller;


import com.rainbowsea.springcloud.entity.Member;
import com.rainbowsea.springcloud.entity.Result;
import com.rainbowsea.springcloud.service.MemberService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class MemberController {

    @Resource
    private MemberService memberService;


    /*
    说明:
         1. 我们的前端如果是以 json 格式来发送添加信息的Member,那么我们需要使用 @RequestBody
         才能将数据封装到对应的 bean,同时保证http的请求的 content-type 是对应
         2. 如果前端是以表单形式提交了,则不需要使用@RequestBody,才会进行对象bean参数封装,同时
         保证 http的请求的 content-type 是对应
     */

    /**
     * 添加方法/接口
     *
     * @param member
     * @return
     */
    @PostMapping("/member/save")
    public Result save(@RequestBody Member member) {
        // 注意:微服务组件通信的坑点:
        // 这里我们使用 RestTemplate 传输发送的数据格式是以 json 格式的所以要添加撒谎给你 @RequestBody
        // 将json 格式的字符串转换为 bean对象进行赋值
        // 同时,我们 bean 对象传输过程中是需要序列化的。
        log.info("member-service-provider-10000 save member={}", member);
        int affected = memberService.save(member);
        if (affected > 0) { // 说明添加成功
            return Result.success("添加会员成功", affected);
        } else {
            return Result.error("401", "添加会员失败");
        }
    }


    /**
     * 这里我们使用 url占位符 + @PathVariable
     *
     * @param id
     * @return
     */
    @GetMapping("/member/get/{id}")
    public Result getMemberById(@PathVariable("id") Long id) {
        Member member = memberService.queryMemberById(id);

        // 使用 Result 把查询到的结果返回
        if (member != null) {
            return Result.success("查询会员成功", member);
        } else {
            return Result.error("402", "ID" + id + "不存在");
        }
    }

}

特别说明:

说明:
1. 我们的前端如果是以 json 格式来发送添加信息的Member,那么我们需要使用 @RequestBody
才能将数据封装到对应的 bean,同时保证http的请求的 content-type 是对应
2. 如果前端是以表单形式提交了,则不需要使用@RequestBody,才会进行对象bean参数封装,同时保证 http的请求的 content-type 是对应

在这里插入图片描述

测试:

测试查询

启动 member-service-provider-10000 模块,打开浏览器输入: 浏览器: http://localhost:10000/member/get/1 看看是否能够查询成功:

在这里插入图片描述

在这里插入图片描述

测试删除:

使用 Postman 看看能否添加成功。

在这里插入图片描述

在这里插入图片描述

2.2.3 注意事项和具体细节

  1. 我们的前端如果是以 json 格式来发送添加信息 furn,那么我们需要使用@RequestBody, 才能将数据封装到对应的 bean, 同时保证 http 的请求头的 content-type 是对应。

在这里插入图片描述

  1. 如果前端是以表单形式提交了/或者是以 parameters,则不需要使用@RequestBody, 才 会进行对象参数封装, 同时保证 http 的请求头的 content-type 是对应
  2. 在进行 SpringBoot 应用程序测试时,引入的 JUnit 是 org.junit.jupiter.api.Test
  3. 在运行程序时,一定要确保你的 XxxxMapper.xml 文件 被自动放到的 target 目录的 classes 指定目录

在这里插入图片描述

2.3 创建使用会员微服务模块 -service consumer

2.3.1 需求说明/图解

在这里插入图片描述

简单的说:就是让我们的 member-service-consumer-80模块,通过调用 member-service-provider-10000 模块的里的方法/接口,处理前端的业务请求。

本质:就是前端提交的业务让我们这个**member-service-provider-10000,服务提供微服务模块处理** 。

浏览器: http://localhost/member/consumer/get/1

在这里插入图片描述

测试添加会员 : http://localhost/member/consumer/save

2.3.2 具体实现步骤

  1. 创建 Moduel(member-service-consumer-80) & 完成配置

创建 member-service-consumer-80 微服务模块[使用会员服务]

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

父工程的 pom.xml-会做相应变化,管理 member-service-consumer-80 微服务 子模块

在这里插入图片描述

  1. 编写 member-service-consumer-80 的 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">
    <!--    parent 父项目/模块内容-->
    <parent>
        <artifactId>e-commerce-center</artifactId>
        <groupId>com.rainbowsea</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>member-service-consumer-80</artifactId>

    <!--    引入相关的依赖:我们引入了当前所需要的依赖,后面如果有其它的需要,再灵活添加-->
    <dependencies>
        <!--        引入 web-starter 说明:我们使用版本仲裁(从父项目继承了版本)-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--1. starter-actuator 是sprng boot 程序的监控系统,可以实现健康检查,info 信息等
         2. 访问http://localhost:10000/actuator 可以看到相关链接,还可以做相关配置-->
        <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>

        <!--        引入 test-starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


    </dependencies>
</project>
  1. 创建 resources/application.yml 配置相关信息

在这里插入图片描述

  1. 创建主启动类 com/rainbowsea/springcloud/MemberConsumerApplication.java

在这里插入图片描述

  1. 在该项目当中的创建一个包为 com.rainbowsea.springcloud.entity 下创建,我们所需要的 Java Bean 的类。两个:Result,以及 Member 两个 java 类。对于 Member 类我们使用 Lombak 插件进行自动生成,而这里的 Result 所需要的方法,需要自定义静态方法,使用 Lombak 自动生成的方法,不够使用,所以 Result 就自己手动编写了。

    在这里插入图片描述

package com.rainbowsea.springcloud.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Member implements Serializable { // Serializable 表示传输时的序列化操作
    private Long id;
    private String name;
    private String pwd;
    private String mobile;
    private String email;
    private Integer gender;
}

在这里插入图片描述

package com.rainbowsea.springcloud.entity;


import java.io.Serializable;

/**
 * 1. 用于返回结果, 利于 json 格式
 * 2. 这个工具类, 在网上也可找到
 */
public class Result<T> implements Serializable {


    private String code; //状态码
    private String msg; //对状态说明
    private T data; // 返回时,携带的数据, 为了扩展性好,老师使用泛型

    //无参构造器
    public Result() {

    }

    //带参构造器-指定返回的data
    public Result(T data) {
        this.data = data;
    }

    //编写方法-返回需要的Result对象-表示成功的Result
    public static Result success() {
        Result result = new Result<>();
        result.setCode("200");
        result.setMsg("success");
        return result;
    }

    //编写方法-返回需要的Result对象-表示成功的Result,同时可以携带数据
    //如果需要在static方法使用泛型,需要在 static <T>
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>(data);
        result.setCode("200");
        result.setMsg("success");
        return result;
    }

    //编写方法-返回需要的Result对象-表示成功的Result,同时可以携带数据和指定msg
    //如果需要在static方法使用泛型,需要在 static <T>
    public static <T> Result<T> success(String msg, T data) {
        Result<T> result = new Result<>(data);
        result.setCode("200");
        result.setMsg(msg);
        return result;
    }

    //编写方法-返回需要的Result对象-表示失败的Result
    public static Result error(String code, String msg) {
        Result result = new Result<>();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }

    public static <T> Result<T> error(String code, String msg, T data) {
        Result<T> result = new Result<>(data);
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    //编写方法-返回需要的Result对象-表示失败的Result,同时可以携带数据

    public void setData(T data) {
        this.data = data;
    }
}




重点: 注入 RestTemplate

RestTemplate 是 Spring 提供的用于访问 Rest 服务的模块类。

  1. RestTemplate 提供了多种便捷访问远程Http服务 的方法
  2. 说明: 大家可以这样理解,通过 RestTemplate ,我们可以发出 Http请求(支持Restful风格), 去调用Controller 提供的 API接口,就像我们使用浏览器发出http请求,调用该 API 接口一样。
  3. 使用简单便捷

对应的官方地址: docs.spring.io/spring-fram…

在这里插入图片描述

创建配置类: com/rainbowsea/springcloud/config/CustomizationBean.java

配置 RestTemplate 模块信息的内容。

在这里插入图片描述

package com.rainbowsea.springcloud.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration  // 标注配置类
public class CustomizationBean {

    // 交给 spring ioc 容器管理
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

配置好 RestTemplate 后,我们就可以写该项目的 Controller 控制器了。

com.rainbowsea.springcloud.controller 包下,创建一个名为 MemberConsumerController 的场景控制器。

在这里插入图片描述

特别说明:


@RestController
@Slf4j
public class MemberConsumerController {


    // 定义 MEMBER_SERVICE_PROVIDER_URL 这是一个基础 url地址
    // 使用 shift+ctrl+u 进行字母大小写的切换
    private static final String MEMBER_SERVICE_PROVIDER_URL = "http://localhost:10000";


    // 装配 RestTemplate bean/对象
    @Resource
    private RestTemplate restTemplate;


    @PostMapping("/member/consumer/save")
    public Result<Member> save(Member member) {
        // 1.第1个参数: 就是请求的完整的url:MEMBER_SERVICE_PROVIDER_URL + "/member/save" => http://localhost:10000/member/save
        // 表示你向将该请求,发送给那个微服务处理,注意无论是返回值,还是参数, @PostMapping() 请求方式都要一一对应上对应处理的微服务上的内容
        //2. 第2个参数: member : 就是通过 restTemplate 发出的 post 请求携带数据或者对象
        //3. 第3个参数: Result.class ,微服务当中的对应处理的方法的,返回值,也就是返回对象类型

        // 注意:坑点
        log.info("member-service-consumer-80 save member={}", member);
        return restTemplate.postForObject
                (MEMBER_SERVICE_PROVIDER_URL + "/member/save", member, Result.class);

    }
}
 // 1.第1个参数: 就是请求的完整的url:MEMBER_SERVICE_PROVIDER_URL + "/member/save" => http://localhost:10000/member/save
        // 表示你向将该请求,发送给那个微服务处理,注意无论是返回值,还是参数, @PostMapping() 请求方式都要一一对应上对应处理的微服务上的内容
        //2. 第2个参数: member : 就是通过 restTemplate 发出的 post 请求携带数据或者对象
        //3. 第3个参数: Result.class ,微服务当中的对应处理的方法的,返回值,也就是返回对象类型

restTemplate.postForObject
                (MEMBER_SERVICE_PROVIDER_URL + "/member/save", member, Result.class);

在这里插入图片描述

重点:特别的这里有个坑点

我们可以先测试运行试试,看看是否能够添加成功数据到数据表当中。

在这里插入图片描述

数据库添加数据失败:我们可以看到:在postman 当中返回显示添加成了,但是我们这里数据表当中,添加的都是 null 值,所以添加失败了。为什么这样呢。

我们查看后端,控制台的 Log.inof 的打印显示的内容如下:

在这里插入图片描述

解决方法:添加会员数据库中为 null 的解决方案

在 1000 端口微服务的 save 方法当中的参数列表当中添加上: @RequestBoday

添加上: @RequestBoday 因为我们使用的 RestTemplate 传输发生的数据的格式是以JSON 格式传输的。

在这里插入图片描述

在这里插入图片描述

运行测试看看是否能够添加成功了,同时看看控制台列表。注意测试的时候,因为这里我们这里修改了源码,要重新启动一下,我们的当前修改的模块。

在这里插入图片描述

member-service-provider-10000 模块当中的 Controller 的完整内容

package com.rainbowsea.springcloud.controller;


import com.rainbowsea.springcloud.entity.Member;
import com.rainbowsea.springcloud.entity.Result;
import com.rainbowsea.springcloud.service.MemberService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class MemberController {

    @Resource
    private MemberService memberService;


    /*
    说明:
         1. 我们的前端如果是以 json 格式来发送添加信息的Member,那么我们需要使用 @RequestBody
         才能将数据封装到对应的 bean,同时保证http的请求的 content-type 是对应
         2. 如果前端是以表单形式提交了,则不需要使用@RequestBody,才会进行对象bean参数封装,同时
         保证 http的请求的 content-type 是对应
     */

    /**
     * 添加方法/接口
     *
     * @param member
     * @return
     */
    @PostMapping("/member/save")
    public Result save(@RequestBody Member member) {
        // 注意:微服务组件通信的坑点:
        // 这里我们使用 RestTemplate 传输发送的数据格式是以 json 格式的所以要添加撒谎给你 @RequestBody
        // 将json 格式的字符串转换为 bean对象进行赋值
        // 同时,我们 bean 对象传输过程中是需要序列化的。
        log.info("member-service-provider-10000 save member={}", member);
        int affected = memberService.save(member);
        if (affected > 0) { // 说明添加成功
            return Result.success("添加会员成功", affected);
        } else {
            return Result.error("401", "添加会员失败");
        }
    }


    /**
     * 这里我们使用 url占位符 + @PathVariable
     *
     * @param id
     * @return
     */
    @GetMapping("/member/get/{id}")
    public Result getMemberById(@PathVariable("id") Long id) {
        Member member = memberService.queryMemberById(id);

        // 使用 Result 把查询到的结果返回
        if (member != null) {
            return Result.success("查询会员成功", member);
        } else {
            return Result.error("402", "ID" + id + "不存在");
        }
    }

}

补充说明:

这里我们对我们的 Java Bean 实现 implementsSerializable 接口,支持反序列化操作。因为 Java Bean 在网络中出传输。

在这里插入图片描述

# 对象流 ObjectInputStream 和 ObjectOutPutStream
看一个需求:
1.int num = 100 这个int 数据保存到文件中,注意不是 100 数字,而是 int 100,并且能够从
文件中直接恢复成 int 100
2.Dog dog = new Dog("小黄",3) 这个 dog 对象保存到文件中,并且能够从文件中恢复成 Dog dog
3. 上面的要求,就是能够将基本数据类型或者对象进行“序列化” 和 “反序列化”

## 序列化 和 反序列化
1.序列化就是在保存数据时,保存数据的值和数据类型
2.反序列化就是在恢复数据时,恢复数据的值和数据类型
3.需要让某个对象,支持序列化,则必须让其类是序列化的,为了让某个类是可序列化的,则
该类必须实现如下“两个接口”的其中一个
* Serializable // 这是一个标记接口,没有方法
* Externalizable // 该接口有方法需要实现,因此我们一般实现上面的 Serializable 接口,即可。

2.3.3 注意事项和具体细节

  1. 如 果 member-service-consumer-80 启 动 报 错 : springBoot 启 动 If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.

在这里插入图片描述

  1. 添加会员数据库中为 null 的解决方案

注意 微服务组件之间通信的的一个坑点 @RequestBody 注意:微服务组件通信的坑点: 这里我们使用 RestTemplate 传输发送的数据格式是以 json 格式的所以要添加撒谎给你 @RequestBody 将json 格式的字符串转换为 bean对象进行赋值同时,我们 bean 对象传输过程中是需要序列化的

在这里插入图片描述

在这里插入图片描述

3. 补充:开启 Run DashBoard

什么是 Run Dashboard

当 springcloud 的服务有多个时,管理多个服务的启动使用 run 会不好管理,这样我们就可以使用 Run Dashboard。如图:

在这里插入图片描述

新版的 2020 的 IDEA 当你同时启动两个微服务时,不会弹出启动 Run Dashboard 窗口的提示,是因为 IDEA2020 将 Run Dashboard 添加到控制台 Service 中

开启 Run Daahboard/Service 的步骤

  1. 找到你的项目/.idea/workspace.xml 文件在其中添加下面的代码即可

在这里插入图片描述

component name="RunDashboard">
<option name="configurationTypes">
<set>
<option value="SpringBootApplicationConfigurationType" />
</set>
</option>
<option name="ruleStates">
<list>
<RuleState>
    <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
  1. 重新启动 idea2020.2 , 会看到如下界面 , 如果没有看到这个 Services, 参考第 3 步添加一下即可。
  2. 如果没有看到这个 Services, 添加一下即可

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 启动你的微服务,就会在 Service 面板上看到各个微服务模块, 也可以进行管理

在这里插入图片描述

提醒: 不同版本的 IDEA 开启 Run DashBoard 有区别,如果 IDEA 版本不同,百度下解决,即可

2.4 创建共用模块-供其它模块使用

2.4.1 需求说明/图解

具体需求如下图:

member-service-provider-10000member-service-consumer-80 两个项目模块当中都有一个共同的 Jave Bean 的内容(Meber 和 Result ) ,我们可以将其共用的共同的部分抽取出来,然后使用 maven 打包成 jar 包,然后引入到其它的模块当中使用,那么其它模块当中的 Java Bean 的内容就可以删除了,使用 打包好的 jar 包即可,提高了减少了代码的冗余度。

在这里插入图片描述

  1. 创建 Moduel & 完成配置
  2. 创建 entity, 把共用的实体类放到对应的包下
  3. 完成测试

2.4.2 具体实现步骤

  1. 创建 e_commerce_center-common-api

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

父工程的 pom.xml-会做相应变化,管理 e_commerce_center-common-api 子模块

在这里插入图片描述

尽量养成好习惯,每次添加的时候,都看上一眼,防止添加失败后,后面一些列的错误,后续找起来也比较麻烦。

  1. 编写e_commerce_center-common-api 的 pom.xml

因为当前我们的这个 api 封装的 member-service-provider-10000member-service-consumer-80 两个项目模块的共同内容,仅仅只是两个 Java Bean 所以我们目前只需要引入:lombok 一个 jar 包即可。

在这里插入图片描述

<!--  lombok 配置-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <!--
                                 <optional>true</optional>
                    1. true 表示两个项目之间依赖不传递
                    2. 大家可以理解 <optional>true</optional>:防止经该依赖传递到其它模块中
                    说的再具体一些,比如: member-service-consumer-80 模块依赖了本项目
               ,那么本项目不会把 lombok 传递给 member-service-consumer-80
                    3. 不设置 optional 或者 optional 是 false ,表示传递依赖
            -->
            <optional>true</optional>
        </dependency>
<?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">
    <parent>
        <artifactId>e-commerce-center</artifactId>
        <groupId>com.rainbowsea</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>e_commerce_center-common-api</artifactId>

    <!--    引入公共模块需要的依赖-->
    <dependencies>
        <!--  lombok 配置-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <!--
                                 <optional>true</optional>
                    1. true 表示两个项目之间依赖不传递
                    2. 大家可以理解 <optional>true</optional>:防止经该依赖传递到其它模块中
                    说的再具体一些,比如: member-service-consumer-80 模块依赖了本项目
               ,那么本项目不会把 lombok 传递给 member-service-consumer-80
                    3. 不设置 optional 或者 optional 是 false ,表示传递依赖
            -->
            <optional>true</optional>
        </dependency>
    </dependencies>


</project>
  1. 抽取共用 API/类 这里我们需要抽取的是:Java Bean 的内容,

注意: 这里我们所设置的包名也尽量和 member-service-provider-10000member-service-consumer-80 两个项目模块的包名一致为com.rainbowsea.springcloud.entity,这样可以避免一些导包的错误。

在这里插入图片描述

package com.rainbowsea.springcloud.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Member implements Serializable { // Serializable 表示传输时的序列化操作
    private Long id;
    private String name;
    private String pwd;
    private String mobile;
    private String email;
    private Integer gender;
}

package com.rainbowsea.springcloud.entity;


import java.io.Serializable;

/**
 * 1. 用于返回结果, 利于 json 格式
 * 2. 这个工具类, 在网上也可找到
 */
public class Result<T> implements Serializable {


    private String code; //状态码
    private String msg; //对状态说明
    private T data; // 返回时,携带的数据, 为了扩展性好,老师使用泛型

    //无参构造器
    public Result() {

    }

    //带参构造器-指定返回的data
    public Result(T data) {
        this.data = data;
    }

    //编写方法-返回需要的Result对象-表示成功的Result
    public static Result success() {
        Result result = new Result<>();
        result.setCode("200");
        result.setMsg("success");
        return result;
    }

    //编写方法-返回需要的Result对象-表示成功的Result,同时可以携带数据
    //如果需要在static方法使用泛型,需要在 static <T>
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>(data);
        result.setCode("200");
        result.setMsg("success");
        return result;
    }

    //编写方法-返回需要的Result对象-表示成功的Result,同时可以携带数据和指定msg
    //如果需要在static方法使用泛型,需要在 static <T>
    public static <T> Result<T> success(String msg, T data) {
        Result<T> result = new Result<>(data);
        result.setCode("200");
        result.setMsg(msg);
        return result;
    }

    //编写方法-返回需要的Result对象-表示失败的Result
    public static Result error(String code, String msg) {
        Result result = new Result<>();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }

    public static <T> Result<T> error(String code, String msg, T data) {
        Result<T> result = new Result<>(data);
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    //编写方法-返回需要的Result对象-表示失败的Result,同时可以携带数据

    public void setData(T data) {
        this.data = data;
    }
}


  1. 使用 Maven 打包成 jar

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

可以解压 e_commerce....jar 可以看到打包后的.class 文件

在这里插入图片描述

member-service-consumer-80 引入 e_commerce_center-common-api-1.0-SNAPSHOT.jar

删除: 删除原来 member-service-provider-10000member-service-consumer-80 两个项目模块 的 entity 包,引入:e_commerce_center-common-api 模块的打包的jar 内容。

在这里插入图片描述

member-service-consumer-80 引入e_commerce_center-common-api 模块的打包的 jar 内容

在这里插入图片描述

member-service-provider-10000 引入e_commerce_center-common-api 模块的打包的 jar 内容。

在这里插入图片描述

        <!--        引入我们自己对 bean 封装成 api 的模块内容-->
        <dependency>
            <groupId>com.rainbowsea</groupId>
            <artifactId>e_commerce_center-common-api</artifactId>
            <version>${project.version}</version>
        </dependency>

2.4.3 测试

  1. 首先启动微服务模块: member-service-provider-10000 和 member-service-consumer-80

浏览器: http://localhost/member/consumer/get/1

在这里插入图片描述

PosMan : 测试添加会员 : http://localhost/member/consumer/save

在这里插入图片描述

3. 总结:

  1. 搭建 Spring Cloud 微服务基本环境简易架构图:

在这里插入图片描述

简单的说就是两个模块,处理业务

  • member-service-consumer-80 : 作为服务消费微服务模块,提供给客户端显示处理,连接浏览器
  • member-service-provider-10000 : 作为服务提供 消费微服务模块,处理前端实际提交的业务,进行处理
  1. 对于一个微服务 的搭建,这里我们采用父工程,利用 Maven版本仲裁 简化配置。

  2. 当我们在父项目/父工程当中配置了该 <dependencyManagement\> 的标签内容,需要注意的一点是: 这里的依赖的 jar 并没有引入到项目当中,仅仅这是一个声明而已 <dependencyManagement> 里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要的添加依赖相对应的依赖信息。

    在这里插入图片描述

  3. 特别说明: type: pom 和 scope: import 配置使用 的作用:让多个模块之间可以多继承,解决 maven 单继承机制。

                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>2.2.2.RELEASE</version>
                    <!--
                     1.type:pom 和 scope: import 配合使用
                     2.表示父项目的子模块,在引入 springboot 相关依赖时,锁定版本为 2.2.2.RELEASE
                     3.通过 pom + import 解决maven单继承机制
                     4. 子模块之间可以多继承-->
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
    
    
  4. dependencyManagement 细节说明

  5. Result 该类用于,前后端交互的一个返回结果,同时利于 json 格式的内容上的转换。这个工具类,我们可以在网上很轻松的找到

    特别的该 Result 类当中有: 三个成员变量:

    • private String code; // 表示后端提交给前端所显示的 :状态码

    • private String msg; // 表示对后端提交的状态码的一个说明解释的内容,比如 404 提示找不到内容的提示 等等

    • private T data; // 表示对后端返回给前端的数据/携带的数据, 为了扩展性好,我们这里使用泛型,任何数据类型基本上都可以满足。

    public class Result<T> implements Serializable {
    
    
        private String code; //状态码
        private String msg; //对状态说明
        private T data; // 返回时,携带的数据, 为了扩展性好,老师使用泛型
    }
    

    如何在静态方法中使用泛型

    我们知道在Java当中 泛型 是在 实例化对象 才确定的,而静态方法 则是,加载类的时候就确定了的,两个内容的加载时期对应不上,所以:直接在静态方法当中使用 泛型是会报错的。如下:

    在这里插入图片描述

    解决方法就是:我们只需要在静态方法中的 static 关键字的后面加上 <T> 其泛型即可。如下:public static <T> Result<T> error(String code, String msg, T data)

    在这里插入图片描述

  6. 注意:在Spring Cloud 和 Spring Boot 当中运行测试类的话:该项目必须要有场景启动器,同时场景启动器添加上了 @SpringBootApplication 注解。才行。

    @SpringBootTest // 如果指明的路径不同;要加上注解指明(场景启动的所在包路径)// 同时一定要有场景启动器注解
    // 如果不想把测试类放到和启动类相同的包下,那就给测试类的注解加上@SpringBootTest(classes = {springbootJpaApplication.class}) 代
    

    在这里插入图片描述

在进行 SpringBoot 应用程序测试时,引入的 JUnit 是 org.junit.jupiter.api.Test

  1. 重点: 注入 RestTemplate

    RestTemplate 是 Spring 提供的用于访问 Rest 服务的模块类。

    1. RestTemplate 提供了多种便捷访问远程Http服务 的方法
    2. 说明: 大家可以这样理解,通过 RestTemplate ,我们可以发出 Http请求(支持Restful风格), 去调用Controller 提供的 API接口,就像我们使用浏览器发出http请求,调用该 API 接口一样。
    3. 使用简单便捷

    对应的官方地址: docs.spring.io/spring-fram…

 // 1.第1个参数: 就是请求的完整的url:MEMBER_SERVICE_PROVIDER_URL + "/member/save" => http://localhost:10000/member/save
        // 表示你向将该请求,发送给那个微服务处理,注意无论是返回值,还是参数, @PostMapping() 请求方式都要一一对应上对应处理的微服务上的内容
        //2. 第2个参数: member : 就是通过 restTemplate 发出的 post 请求携带数据或者对象
        //3. 第3个参数: Result.class ,微服务当中的对应处理的方法的,返回值,也就是返回对象类型

restTemplate.postForObject
                (MEMBER_SERVICE_PROVIDER_URL + "/member/save", member, Result.class);

在这里插入图片描述

  1. 注意 微服务组件之间通信的的一个坑点 @RequestBody注意:微服务组件通信的坑点: 这里我们使用 RestTemplate 传输发送的数据格式是以 json 格式的所以要添加撒谎给你 @RequestBody 将json 格式的字符串转换为 bean对象进行赋值同时,我们 bean 对象传输过程中是需要序列化的

    在这里插入图片描述

  2. 如何开启 IDEA 的 Run DashBoard

4. 最后:

“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”

在这里插入图片描述