2. Spring Cloud 微服务基础环境搭建
文章目录
- 2. Spring Cloud 微服务基础环境搭建
- 前言
- 1. 微服务需求解析
- 2. 具体搭建微服务步骤:
- 2.1 创建父工程 ,用于聚合其它微服务模块
- 2.1.1 需求说明/图解
- 2.1.2 具体实现步骤
- 2.1.3 注意事项和具体细节
- 2.2 创建会员中心微服务模块 -service provider
- 2.2.1 需求说明/图解
- 2.2.2 具体实现步骤:
- 2.2.3 注意事项和具体细节
- 2.3 创建使用会员微服务模块 -service consumer
- 2.3.1 需求说明/图解
- 2.3.2 具体实现步骤
- 2.3.3 注意事项和具体细节
- 3. 补充:开启 Run DashBoard
- 2.4 创建共用模块-供其它模块使用
- 2.4.1 需求说明/图解
- 2.4.2 具体实现步骤
- 2.4.3 测试
- 3. 总结:
- 4. 最后:
前言
- 对应上一篇学习内容:🌟🌟🌟 1. 初始认识 Spring Cloud-CSDN博客
- 对应下一篇学习内容:🌟🌟🌟
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 具体实现步骤
- 我们需要先创建一个父项目/父工程 ,该父工程会去管理我们父工程下的多个微服务模块(module) ,如下图所示:( 该图是我们学习微服务内容的所有内容上该微服务配置好的内容 )
- 创建父项目步骤:- 灵活配置方式。
特别的: 当我们创建一个项目成功后,要修改对应该项目所选配的配置。比如:
- 指明该项目我们所要运行的 Java 的版本,这里我们选择 Java 8 的版本
- 配置我们项目的字符集的编码,这里我们选择配置 utf-8 的编码
- 配置指明我们本地的 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.RELEASE3.通过 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.RELEASE3.通过 pom + import 解决maven单继承机制4. 子模块之间可以多继承--><type>pom</type><scope>import</scope></dependency>
删除不需要的配置节点。
这里我们删除
build
和reporting
节点下的内容信息即可。
2.1.3 注意事项和具体细节
dependencyManagement 细节说明
- Maven 使用 <dependencyManagement> 元素来提供了一种管理依赖版本号的方式,
通常在项目 packaging为POM,中使用 <dependencyManagement> 元素 - 使用 pom.xml 中的 <dependencyManagement> 标签元素能让所有在子项目中引用一个依赖,
Maven 会沿着父子层次向上走,直到找到一个拥有 <dependencyManagement> 标签元素的项目,
然后它就会使用这个 <dependencyManagement> 元素中指定的版本号。
3.好处: 如果有多个项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号,
当升级或切换到另一个版本时,只需要在顶层父容器更新,而不需要分别在子项目的修改;另外
如果某个子项目需要另外一个版本,只需要声明 version 就可以。 - <dependencyManagement> 里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要的依赖
- 如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且 version 和 scope 都读取自父
pom
所配置的 <dependencyManagement> 的内容。
- 如果子项目中指定了版本号,那么会使用子项目中指定的 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: MySQL123username: rooturl: 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() {}//带参构造器-指定返回的datapublic Result(T data) {this.data = data;}//编写方法-返回需要的Result对象-表示成功的Resultpublic 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对象-表示失败的Resultpublic 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 mapperPUBLIC "-//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 {@Resourceprivate MemberDao memberDao;@Resourceprivate MemberService memberService;@Test // 注意选择: org.junit.jupiter.api.Test; 包当中的// 注意方法不能定义为 private 私有的,不然无法测试运行的public void testQueryMemberById() {Member member = memberDao.queryMemberById(1L);log.info("member={}", member);}@Testpublic 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);}@Testpublic 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 {@Resourceprivate 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 注意事项和具体细节
- 我们的前端如果是以 json 格式来发送添加信息 furn,那么我们需要使用@RequestBody, 才能将数据封装到对应的 bean, 同时保证 http 的请求头的 content-type 是对应。
- 如果前端是以表单形式提交了/或者是以 parameters,则不需要使用@RequestBody, 才 会进行对象参数封装, 同时保证 http 的请求头的 content-type 是对应
- 在进行 SpringBoot 应用程序测试时,引入的 JUnit 是 org.junit.jupiter.api.Test
- 在运行程序时,一定要确保你的 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 具体实现步骤
- 创建 Moduel(member-service-consumer-80) & 完成配置
创建 member-service-consumer-80 微服务模块[使用会员服务]
父工程的 pom.xml-会做相应变化,管理 member-service-consumer-80 微服务 子模块
- 编写 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>
- 创建 resources/application.yml 配置相关信息
- 创建主启动类 com/rainbowsea/springcloud/MemberConsumerApplication.java
-
在该项目当中的创建一个包为
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() {}//带参构造器-指定返回的datapublic Result(T data) {this.data = data;}//编写方法-返回需要的Result对象-表示成功的Resultpublic 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对象-表示失败的Resultpublic 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 服务的模块类。
- RestTemplate 提供了多种便捷访问远程Http服务 的方法
- 说明: 大家可以这样理解,通过 RestTemplate ,我们可以发出 Http请求(支持Restful风格),
去调用Controller 提供的 API接口,就像我们使用浏览器发出http请求,调用该 API 接口一样。 - 使用简单便捷
对应的官方地址: https://docs.spring.io/spring-framework/docs/6.0.11/javadoc-api/org/springframework/web/client/RestTemplate.html
创建配置类: 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 容器管理@Beanpublic 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/对象@Resourceprivate 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 {@Resourceprivate 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 实现
implements
上Serializable
接口,支持反序列化操作。因为 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 注意事项和具体细节
- 如 果 member-service-consumer-80 启 动 报 错 : springBoot 启 动 If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
- 添加会员数据库中为 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 的步骤
- 找到你的项目/.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>
- 重新启动 idea2020.2 , 会看到如下界面 , 如果没有看到这个 Services, 参考第 3 步添加一下即可。
- 如果没有看到这个 Services, 添加一下即可
- 启动你的微服务,就会在 Service 面板上看到各个微服务模块, 也可以进行管理
提醒: 不同版本的 IDEA 开启 Run DashBoard 有区别,如果 IDEA 版本不同,百度下解决,即可 。
2.4 创建共用模块-供其它模块使用
2.4.1 需求说明/图解
具体需求如下图:
在
member-service-provider-10000
和member-service-consumer-80
两个项目模块当中都有一个共同的 Jave Bean 的内容(Meber 和 Result ) ,我们可以将其共用的共同的部分抽取出来,然后使用 maven 打包成jar
包,然后引入到其它的模块当中使用,那么其它模块当中的 Java Bean 的内容就可以删除了,使用 打包好的 jar 包即可,提高了减少了代码的冗余度。
- 创建 Moduel & 完成配置
- 创建 entity, 把共用的实体类放到对应的包下
- 完成测试
2.4.2 具体实现步骤
- 创建 e_commerce_center-common-api
父工程的 pom.xml-会做相应变化,管理 e_commerce_center-common-api 子模块
尽量养成好习惯,每次添加的时候,都看上一眼,防止添加失败后,后面一些列的错误,后续找起来也比较麻烦。
- 编写e_commerce_center-common-api 的 pom.xml
因为当前我们的这个 api 封装的 member-service-provider-10000
和 member-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-803. 不设置 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-803. 不设置 optional 或者 optional 是 false ,表示传递依赖--><optional>true</optional></dependency></dependencies></project>
- 抽取共用 API/类 这里我们需要抽取的是:Java Bean 的内容,
注意: 这里我们所设置的包名也尽量和 member-service-provider-10000
和 member-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() {}//带参构造器-指定返回的datapublic Result(T data) {this.data = data;}//编写方法-返回需要的Result对象-表示成功的Resultpublic 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对象-表示失败的Resultpublic 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;}
}
- 使用 Maven 打包成 jar
可以解压 e_commerce…jar 可以看到打包后的.class 文件
在 member-service-consumer-80
引入 e_commerce_center-common-api-1.0-SNAPSHOT.jar
删除: 删除原来 member-service-provider-10000
和 member-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 测试
- 首先启动微服务模块: member-service-provider-10000 和 member-service-consumer-80
浏览器: http://localhost/member/consumer/get/1
PosMan : 测试添加会员 : http://localhost/member/consumer/save
3. 总结:
- 搭建 Spring Cloud 微服务基本环境简易架构图:
简单的说就是两个模块,处理业务
- member-service-consumer-80 : 作为服务消费微服务模块,提供给客户端显示处理,连接浏览器
- member-service-provider-10000 : 作为服务提供 消费微服务模块,处理前端实际提交的业务,进行处理
-
对于一个微服务 的搭建,这里我们采用父工程,利用 Maven 的版本仲裁 简化配置。
-
当我们在父项目/父工程当中配置了该
<dependencyManagement\>
的标签内容,需要注意的一点是: 这里的依赖的 jar 并没有引入到项目当中,仅仅这是一个声明而已
<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.RELEASE3.通过 pom + import 解决maven单继承机制4. 子模块之间可以多继承--><type>pom</type><scope>import</scope></dependency>
-
dependencyManagement 细节说明
-
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)
-
-
注意:在Spring Cloud 和 Spring Boot 当中运行测试类的话:该项目必须要有场景启动器,同时场景启动器添加上了
@SpringBootApplication
注解。才行。@SpringBootTest // 如果指明的路径不同;要加上注解指明(场景启动的所在包路径)// 同时一定要有场景启动器注解 // 如果不想把测试类放到和启动类相同的包下,那就给测试类的注解加上@SpringBootTest(classes = {springbootJpaApplication.class}) 代
在进行 SpringBoot 应用程序测试时,引入的 JUnit 是 org.junit.jupiter.api.Test
-
重点: 注入
RestTemplate
RestTemplate 是 Spring 提供的用于访问 Rest 服务的模块类。
- RestTemplate 提供了多种便捷访问远程Http服务 的方法
- 说明: 大家可以这样理解,通过 RestTemplate ,我们可以发出 Http请求(支持Restful风格),
去调用Controller 提供的 API接口,就像我们使用浏览器发出http请求,调用该 API 接口一样。 - 使用简单便捷
对应的官方地址: https://docs.spring.io/spring-framework/docs/6.0.11/javadoc-api/org/springframework/web/client/RestTemplate.html
// 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);
-
注意 微服务组件之间通信的的一个坑点 @RequestBody注意:微服务组件通信的坑点:
这里我们使用 RestTemplate 传输发送的数据格式是以 json 格式的所以要添加撒谎给你 @RequestBody 将json 格式的字符串转换为 bean对象进行赋值同时,我们 bean 对象传输过程中是需要序列化的 -
如何开启 IDEA 的 Run DashBoard
4. 最后:
“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”