SpringCloud基础

SpringCloud基础环境

1、基本环境版本选择

  • Java: Java17+;
  • spring cloud:2023.0.0;
  • spring boot:3.2.0;
  • cloud alibaba:2022.0.0.0-RC2;
  • Maven:3.9+;
  • Mysql:8.0+;

2、SpringCloud各组件

​ 最新版的SpringCloud各服务组件与过去的版本变化很大,但是现在成熟的系统还得是原来的版本,所以过去版本的服务组件还是要会的。

image-20240306093952638

3、微服务架构编码Base工程模块构建

3.1、整体业务需求说明

image-20240306094452291

3.2、IDE新建Project和Maven父工程

3.2.1、创建的父工程结构:

image-20240306152506246

3.2.2、父工程依赖项

<?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.cloud</groupId><artifactId>mscloudV5</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.26</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>

3.2.3、数据库

​ 新建一个数据库db2024,执行以下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;

image-20240306153740496

3.2.4、创建子工程mybatis_generator

​ 在父工程下新建一个maven项目模块,创建完成后的目录结构如下:

image-20240306194220453

​ 给mybatis_generator模块添加依赖,这个模块就是使用mybatis的快速生成工具mapper4一键生成DAO层的。

​ 下面导入依赖:

<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>

​ 配置文件config.properties,就是数据库配置文件,数据库驱动,我的MySQL8的端口是3307,MySQL5是3306,同时运行了。

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

​ 配置文件generatorConfig.xml,配置自动生成的配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfigurationPUBLIC "-//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>

​ 准备工作完成之后就可以使用mybatis的插件进行自动生成了

image-20240306200812153

​ 执行之后DAO层的东西就有了

image-20240306201018594

3.3、Rest通用Base工程构建

3.3.1、新增module

​ 我们再建一个模块cloud-provider-payment8001支付模块是微服务的提供者。

image-20240306201847823

微服务小口诀:

  1. 建module;
  2. 改POM;
  3. 写YML;
  4. 主启动类;
  5. 业务;

​ 改POM文件

 <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></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>

​ 在resources下新建application.yml文件

server:port: 8001# ==========applicationName + druid-mysql8 driver===================
spring:application:name: cloud-payment-servicedatasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3307/db2024?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=trueusername: rootpassword: 123456# ========================mybatis===================
mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.zm.cloud.entitiesconfiguration:map-underscore-to-camel-case: true

​ 顺便把resources下的mapper文件夹建了,然后把它自动生成的主启动类给改名为Main8001,然后把它的内容改成spring的启动类。

@SpringBootApplication
@MapperScan("com.zm.cloud.mapper")//这里导的是tk.mybatis.spring.annotation.MapperScan
public class Main8001 {public static void main(String[] args) {SpringApplication.run(Main8001.class,args);}
}

​ 把自动生成的实体类和mapper接口及配置文件复制到支付模块中的cloud目录下,就可以把原来mybatis_generator里面自动生成的删除了。

image-20240307103121474

​ 我们对外暴露的不能是数据库表中的全部数据,像密码等敏感数据不能直接提供给前端,所以在实体类中添加一个PayDTO实体类只写部分数据。在自己写测试项目情况下,使用简单的实体对象 Pay可能更加直观和简洁。DTO 的使用通常是为了解耦不同层之间的依赖,同时可以控制数据的传输和保护隐私信息。但如果情境简单且没有太多层次的依赖关系,直接使用实体对象也是完全合理的做法。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PayDTO implements Serializable {private Integer id;//支付流水号private String payNo;//订单流水号private String orderNo;//用户账号IDprivate Integer userId;//交易金额private BigDecimal amount;
}

3.3.2、service层

​ 然后创建server层,创建service接口PayService

public interface PayService {//添加public int add(Pay pay);public int delete(Integer id);public int update(Pay pay);public Pay getById(Integer id);public List<Pay> getAll();}

​ 有了接口就要有实现类,PayServiceImp

@Service
public class PayServiceImp implements PayService {@Resourceprivate PayMapper payMapper;@Overridepublic int add(Pay pay) {return payMapper.insertSelective(pay);}@Overridepublic int delete(Integer id) {return payMapper.deleteByPrimaryKey(id);}@Overridepublic int update(Pay pay) {return payMapper.updateByPrimaryKeySelective(pay);}@Overridepublic Pay getById(Integer id) {return payMapper.selectByPrimaryKey(id);}@Overridepublic List<Pay> getAll() {return payMapper.selectAll();}
}

3.3.3、controller层

​ 新增controller层,PayController

@RestController
public class PayController {@Resourceprivate PayServiceImp payServiceImp;//增加数据@RequestBody包含请求体@PostMapping(value = "/pay/add")public String addPay(@RequestBody Pay pay){System.out.println(pay.toString());int i = payServiceImp.add(pay);return "新增一个记录返回值:"+i;}//删除@DeleteMapping(value = "/pay/del/{id}")public String delByID(@PathVariable Integer id){return "成功删除:"+payServiceImp.delete(id);}//修改,但不能修改全部@PutMapping(value = "/pay/update")public String update(@RequestBody PayDTO payDTO){//创建真实体类对象pay,然后把payDTO的json串复制给它Pay pay = new Pay();BeanUtil.copyProperties(payDTO,pay);return "修改成功返回值:" + payServiceImp.update(pay);}//通过id查询@GetMapping("/pay/get/{id}")public Pay payById(@PathVariable Integer id){return payServiceImp.getById(id);}//查询所有消息@GetMapping("/pay/getall")
public List<PayDTO> getAll() {List<PayDTO> list = new ArrayList<>();for (Pay pay : payServiceImp.getAll()) {PayDTO payDTO = new PayDTO(); // 在循环内创建新的 PayDTO 对象BeanUtil.copyProperties(pay, payDTO);list.add(payDTO);System.out.println("查询到的消息:" + pay.toString());}return list;
}
}

​ 写完controller我们来测试一下,把服务启动,然后在idea中就可以直接测试

image-20240307153417454

​ 交给前端不给那么多信息就行,根据id查询我们就做测试,给出所有数据。

3.3.4、PostMan测试

​ 下面测试其他的请求,由于普通的浏览器不支持post方法,我们就使用PostMan或者SwaggerAPI测试工具来测试剩下的请求。

image-20240307154739300

​ 发送请求,查看结果

image-20240307160312656

​ 再通过id查询出来

image-20240307160429506

​ 然后再修改它

image-20240307160700136

​ 修改成功后再使用查询全部,全查询出来,只不过是部分信息。

image-20240307160835626

​ 查询出来的内容没有问题,我们把第二条记录通过id删除掉

image-20240307160953433

​ 再查询所有,看是否删除。

image-20240307161027378

3.3.5、使用Swagger测试

​ 依赖在之前已经导过了就不再重复,我们需要写一个swagger的配置类,Swagger3Config

​ 常用的swagger注解说明:

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

​ Swagger3Config

@Configuration
public class Swagger3Config {@Beanpublic GroupedOpenApi PayApi() {return GroupedOpenApi.builder().group("支付微服务模块").pathsToMatch("/pay/**").build();}@Beanpublic GroupedOpenApi OtherApi(){return GroupedOpenApi.builder().group("其它微服务模块").pathsToMatch("/other/**", "/others").build();}/*@Beanpublic GroupedOpenApi CustomerApi(){return GroupedOpenApi.builder().group("客户微服务模块").pathsToMatch("/customer/**", "/customers").build();}*/@Beanpublic OpenAPI docsOpenApi(){return new OpenAPI().info(new Info().title("cloud2024").description("通用设计rest").version("v1.0")).externalDocs(new ExternalDocumentation().description("www.zm.com").url("https://yiyan.baidu.com/"));}
}

​ 修改controller,打上swagger的说明注解

@RestController
@Tag(name = "支付订单模块",description = "支付的CRUD")
public class PayController {@Resourceprivate PayServiceImp payServiceImp;//增加数据@RequestBody包含请求体//summary大概说明,description具体描述@PostMapping(value = "/pay/add")@Operation(summary = "新增",description = "新增支付流水方法,json串做参数")public String addPay(@RequestBody Pay pay){System.out.println(pay.toString());int i = payServiceImp.add(pay);return "新增一个记录返回值:"+i;}//删除@DeleteMapping(value = "/pay/del/{id}")@Operation(summary = "删除",description = "删除支付流水方法")public String delByID(@PathVariable("id") Integer id){return "成功删除:"+payServiceImp.delete(id);}//修改,但不能修改全部@PutMapping(value = "/pay/update")@Operation(summary = "修改",description = "修改支付流水方法")public String update(@RequestBody PayDTO payDTO){//创建真实体类对象pay,然后把payDTO的json串复制给它Pay pay = new Pay();BeanUtil.copyProperties(payDTO,pay);return "修改成功返回值:" + payServiceImp.update(pay);}//通过id查询@GetMapping("/pay/get/{id}")@Operation(summary = "查询",description = "通过ID查询")public Pay payById(@PathVariable("id") Integer id){return payServiceImp.getById(id);}//查询所有消息@GetMapping("/pay/getall")@Operation(summary = "查询所有",description = "查到所有简要信息")public List<PayDTO> getAll(){List<PayDTO> list = new ArrayList<>();for (Pay pay : payServiceImp.getAll()) {PayDTO payDTO = new PayDTO();BeanUtil.copyProperties(pay,payDTO);list.add(payDTO);System.out.println("查询到的消息:"+ pay.toString());}return list;}
}

​ 给实体类打上@Schema(title = “Pay实体类”),每一个属性上也打上对应的说明,打上完所有的说明之后就可以启动服务,访问http://localhost:8001/swagger-ui/index.html

image-20240307195643495

​ 由于我们在配置类中写了俩模块,选择支付订单模块。

image-20240307195733417

image-20240307200611403

​ 下面就开始测试,新增一条数据。

{"payNo": "1321testadd","orderNo": "53845u389testadd","userId": 2,"amount": 1999,"deleted": "0","createTime": "2024-03-07T12:06:56.493Z","updateTime": "2024-03-07T12:06:56.493Z"
}

image-20240307200919732

​ 查询所有

image-20240307201002229

​ 修改

image-20240307202444377

​ 通过id查询

image-20240307202520681

​ 删除掉

image-20240307202540377

潜在问题

  • 时间格式,不太符合我们正常看时间的显示方式;

    一般我们有两种解决方法

    1. 可以在相应的时间属性上加上注解@JsonFormat注解,然后写上格式。

      @Column(name = "create_time")
      @Schema(title = "创建时间")
      @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
      private Date createTime;@Column(name = "update_time")@Schema(title = "更新时间")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date updateTime;
      
    2. 在Spring Boot项目中可以在application.yml文件中指定格式。

      spring:jackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8
      
  • Java如何设计API接口实现统一格式返回,我们现在设置的controller的返回值有string、有int、还有实体、还有list;

  • 全局异常接入返回的标准格式,假如我们程序出错了,爆出的错误要有统一返回值和全局统一异常;

3.3.6、API接口实现统一格式返回

定义返回标准格式的三个基本

  • code状态值:后端定义统一定义各种返回结果的状态码;
  • message描述:本次接口调用的结果描述;
  • data数据:本次返回的数据;
  • 一般还得加上接口调用时间,timestamp,在调试过程中得结果可能是由于缓存造成的结果不变,还可以调查错误出现的时间,每次返回的时间戳不一样就代表不是缓存。

HTTP请求返回的状态码

image-20240308140904444

新建一个枚举类ReturnCodeEnum

//@Getter获取全部值的get方法
@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;//2、构造ReturnCodeEnum(String code, String message) {this.code = code;this.message = message;}//3、遍历//3.1、遍历第一种方式,传统的public static ReturnCodeEnum getReturnCodeEnumV1(String code){//循环遍历,如果传进来的code值和遍历出来的有一样的就说明是我们定义的,存在就返回。for (ReturnCodeEnum element : ReturnCodeEnum.values()) {if (element.getCode().equalsIgnoreCase(code)){return element;}}return null;}//3.2、stream流式计算public static ReturnCodeEnum getReturnCodeEnumV2(String code){return Arrays.stream(ReturnCodeEnum.values()).filter(x -> x.getCode().equalsIgnoreCase(code)).findFirst().orElse(null);}//验证/* public static void main(String[] args) {System.out.println(ReturnCodeEnum.getReturnCodeEnumV1("200"));System.out.println(ReturnCodeEnum.getReturnCodeEnumV1("200").getCode());System.out.println(ReturnCodeEnum.getReturnCodeEnumV1("200").getMessage());System.out.println("---------下面是v2版本--------");System.out.println(ReturnCodeEnum.getReturnCodeEnumV1("404"));System.out.println(ReturnCodeEnum.getReturnCodeEnumV1("404").getCode());System.out.println(ReturnCodeEnum.getReturnCodeEnumV1("404").getMessage());}*/}

​ 下面是测试结果:

image-20240308152857548

新建统一的返回对象ResultData

​ 使用这个对象就可以统一给前端返回ResultData,前端只用收这一个对象就行了,后端想传的数据类型以泛型的方式返回,再包上一层。

@Data
@Accessors(chain = true)//@Accessors(chain = true)推荐使用链式编程
public class ResultData<T> {private String code;//结果状态 ,具体状态码参见枚举类ReturnCodeEnum.javaprivate String message;private T data;private long timestamp;public ResultData(){this.timestamp = System.currentTimeMillis();}//成功的public static <T> ResultData <T> success(T data){ResultData resultData = new ResultData();resultData.setCode(ReturnCodeEnum.RC200.getCode());resultData.setMessage(ReturnCodeEnum.RC200.getMessage());resultData.setData(data);return resultData;}//失败的public static <T> ResultData <T> fail(String code,String message){ResultData resultData = new ResultData();resultData.setCode(code);resultData.setMessage(message);resultData.setData(null);return resultData;}
}

3.3.7、修改PayController

@RestController
@Tag(name = "支付订单模块",description = "支付的CRUD")
public class PayController {@Resourceprivate PayServiceImp payServiceImp;//增加数据@RequestBody包含请求体//summary大概说明,description具体描述@PostMapping(value = "/pay/add")@Operation(summary = "新增",description = "新增支付流水方法,json串做参数")public ResultData<String> addPay(@RequestBody Pay pay){System.out.println(pay.toString());int i = payServiceImp.add(pay);return ResultData.success("新增一个记录返回值:"+i);}//删除@DeleteMapping(value = "/pay/del/{id}")@Operation(summary = "删除",description = "删除支付流水方法")public ResultData<String> delByID(@PathVariable("id") Integer id){int i = payServiceImp.delete(id);return ResultData.success("成功删除:"+i);}//修改,但不能修改全部@PutMapping(value = "/pay/update")@Operation(summary = "修改",description = "修改支付流水方法")public ResultData<String> update(@RequestBody PayDTO payDTO){//创建真实体类对象pay,然后把payDTO的json串复制给它Pay pay = new Pay();BeanUtil.copyProperties(payDTO,pay);return ResultData.success("修改成功返回值:"+payServiceImp.update(pay));}//通过id查询@GetMapping("/pay/get/{id}")@Operation(summary = "查询",description = "通过ID查询")public ResultData<Pay> payById(@PathVariable("id") Integer id){return ResultData.success(payServiceImp.getById(id));}//查询所有消息@GetMapping("/pay/getall")@Operation(summary = "查询所有",description = "查到所有简要信息")public ResultData<List<PayDTO>> getAll(){List<PayDTO> list = new ArrayList<>();for (Pay pay : payServiceImp.getAll()) {PayDTO payDTO = new PayDTO();BeanUtil.copyProperties(pay,payDTO);list.add(payDTO);System.out.println("查询到的消息:"+ pay.toString());}return ResultData.success(list);}
}

​ 再进行测试,新增一条记录

image-20240308162632483

​ 修改

image-20240308165755252

​ 通过id查询,4号

image-20240308165831951

​ 查询所有

image-20240308165850945

​ 通过id删除

image-20240308165914371

​ 如果我们在查询的时候查的是错误的,比如-4这个时候前端显示的就是错误页面,很不美观,还破环了我们的统一封装格式。

image-20240308170124476

3.3.8、全局异常接入返回的标准格式

​ 定义了全局异常处理就不需要自己try catch了,那样麻烦,新建一个处理异常的类,GlobalExceptionHandler

//默认全局异常处理
@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());}
}

​ 这个时候我们启动服务,访问正常的和错误的页面

image-20240308172729387

3.4、cloud-consumer-order80微服务调用者订单Module模块

3.4.1、新建模块cloud-consumer-order80

image-20240308174439317

添加依赖

 <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>

新建application.yml文件

server:port:  80

​ 订单微服务80如何才能调用到支付微服务8001?

3.4.2、RestTemplate

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

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

​ config配置类,RestTemplateConfig

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

​ 新建OrderController

@RestController
public class OrderController {//先写死,支付模块的地址private static final String PAY_MAN_SERVER = "http://localhost:8001/";@Autowiredprivate RestTemplate restTemplate;//添加订单//一般情况下,通过浏览器的地址栏输入url,发送的只能是get请求//我们底层调用的是post方法,模拟消费者发送get请求,客户端消费者//参数可以不添加@RequestBody@GetMapping("/consumer/pay/add")public ResultData addOrder(PayDTO payDTO){return restTemplate.postForObject(PAY_MAN_SERVER + "/pay/add",payDTO,ResultData.class);}//通过id查询@GetMapping("/consumer/pay/get/{id}")public ResultData getOrderById(@PathVariable("id") Integer id){return restTemplate.getForObject(PAY_MAN_SERVER + "/pay/get/"+id,ResultData.class,id);}//修改@GetMapping("/consumer/pay/update")public ResultData updateOrder(PayDTO payDTO){return restTemplate.postForObject(PAY_MAN_SERVER + "/pay/update",payDTO,ResultData.class);}//删除@GetMapping("/consumer/pay/del/{id}")public ResultData delById(@PathVariable("id") Integer id){return restTemplate.postForObject(PAY_MAN_SERVER + "/pay/del/"+id,id,ResultData.class);}//查询所有@GetMapping("/consumer/pay/getall")public ResultData getAll(){return restTemplate.getForObject(PAY_MAN_SERVER + "/pay/getall",ResultData.class);}
}

​ 启动postman测试,新增

image-20240309170550910

​ 查询所有

image-20240309170623183

3.5、重构

​ 系统中重复的内容太多了,我们希望提取出公共的类,每一个模块都可以共享,就不要再复制了,新建一个module,cloud-api-commons,它对外暴露通用的组件/api/接口/工具类等等。

​ 导入一点点依赖

<dependencies><!--SpringBoot通用依赖模块--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency></dependencies>

​ 把公共使用的实体类PayDTO和返回信息工具类resp目录中的ResultData和ReturnCodeEnum拷贝到模块下,还有统一异常处理。

image-20240309171808784

​ 然后我们使用maven的install命令,自定义依赖

image-20240309173011735

​ 然后把80客户端服务和8001支付服务的公共部分删除掉,在pom中添加这个自定义的依赖。

<!-- 引入自己定义的api通用包 -->
<dependency><groupId>com.zm.cloud</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version>
</dependency>

​ 然后再分别启动这两个服务测试一下能不能使用。

image-20240309173510063

​ 再添加一个记录

image-20240309173641831

​ 通过id查询

image-20240309195332922

​ 其他操作就不再一一演示,下面看一下当前工作进展

image-20240309202044216

​ 我们目前只不过是最基础的内容,SpringCloud的内容还没提及呢,当前有很多问题。

//先写死,支付模块的地址private static final String PAY_MAN_SERVER = "http://localhost:8001/";

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

  1. 如果订单微服务和支付微服务的IP地址或者端口号发生了变化,则支付微服务将变得不可用,需要同步修改订单微服务中调用支付微服务的IP地址和端口号,就很麻烦了。
  2. 如果系统中提供了多个订单微服务和支付微服务,则无法实现微服务的负载均衡功能,这都写死了,怎么做负载。
  3. 如果系统需要支持高并发,需要部署更多的订单微服务和支付微服务,硬编码订单微服务则后续的维护会变得异常复杂。
  4. 所以,在微服务的开发过程中,需要引入服务治理功能,实现微服务之间的动态注册与发现,从此刻开始我们正式进入SpringCloud实战。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/732632.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ Qt开发:QNetworkInterface网络接口组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍如何运用QNetworkInterface组件实现查询详细的…

Ubuntu18/20运行ORB-SLAM3

ORB-SLAM3复现(ubuntu18/20) 文章目录 ORB-SLAM3复现(ubuntu18/20)1 坐标系与外参Intrinsic parameters2 内参Intrinsic parameters2.1 相机内参① 针孔模型Pinhole② KannalaBrandt8模型③ Rectified相机 2.2 IMU内参 3 VI标定—外参3.1 Visual calibration3.2 Inertial calib…

软件测试工程师的一天工作内容是什么?

相信但凡提出这个问题&#xff0c;基本上都是准备入行或转行&#xff0c;又各种纠结的小伙伴。测试工程师到底都些干啥&#xff1f;加班多不多?薪资高不高?前景怎么样?有没有35岁焦虑问题&#xff1f;让我们挨个问题盘一下。 一、测试人员的日常 9&#xff1a;30 到公司 打…

[贰],万能开篇HelloWorld

1&#xff0c;新建项目 File/New/Project Android/Android Application Project 输入程序名字HelloWorld Next Next 选择Blank Activity 修改为HelloWorldActivity 2&#xff0c;异常点 No resource found that matches the given name Theme.AppCompat.Light import andro…

vue3引入高德地图

首先注册高德key https://console.amap.com/dev/key/a vue项目中安转地图包 pnpm i amap/amap-jsapi-loader -S 先说最重要核心&#xff0c;踩雷过 页面中需写入以下代码&#xff0c;现在注册的高德key要求强制写入安全密钥 window._AMapSecurityConfig {securityJsCode…

CorelDRAW Essentials2024使用简单易学的图形软件,让设计充满乐趣

CorelDRAW Essentials 2024使用简单易学的图形软件&#xff0c;让设计充满乐趣 创作引人注目的海报、卡片、社交媒体图片等。 增强功能&#xff01;支持文件导入/导出新增功能&#xff01;支持 WebP 文件&#xff0c;提高网页兼容性并优化图像交付增强功能&#xff01;显著的…

Independent Variable Dependent Variable

自变量&#xff08;Independent Variable&#xff09; -----------> 因变量&#xff08;Dependent Variable&#xff09; 数据 ----------------------------------------------结果&#xff0c;报告等等

Raingad IM即时聊天/即时通讯网站源码,附带系统搭建教程

支持功能 支持单聊和群聊&#xff0c;支持发送表情、图片、语音、视频和文件消息单聊支持消息已读未读的状态显示&#xff0c;在线状态显示群聊创建、删除和群成员管理、群公告、群禁言等支持置顶联系人&#xff0c;消息免打扰&#xff1b;支持设置新消息声音提醒&#xff0c;…

FPGA的配置状态字寄存器Status Register

目录 简介 状态字定义 Unknown Device/Many Unknow Devices 解决办法 一般原因 简介 Xilinx的FPGA有多种配置接口&#xff0c;如SPI&#xff0c;BPI&#xff0c;SeletMAP&#xff0c;Serial&#xff0c;JTAG等&#xff1b;如果从时钟发送者的角度分&#xff0c;还可以…

【绿色碳中和】各国城市历年空气污染指标数据集(2017-2023)

一、有关“空气污染”的发文趋势和主题分布 全新整理的2017-2023年各国城市空气污染实证数据&#xff0c;供大家研究使用。 二、数据来源&#xff1a;IQAir官网&#xff08;存在部分缺失值&#xff09; 三、时间跨度&#xff1a;2017-2023年 四、数据范围&#xff1a;世界各国…

MybatisPlus知识点总结(基于黑马2023MybatisPlus课程)

MybatisPlus知识点总结 配套资料 黑马微服务框架笔记&#xff0c;内含mpMybatisPlus.pptxMyBatis-Plus &#xff08;mp中文官网&#xff09; 快速入门 入门案例 使用MybatisPlus的基本步骤&#xff1a;1.引入MybatisPlus依赖&#xff0c;代替Mybatis依赖2.定义Mapper接口并继…

Ubuntu上安装任意版本nodejs方法

在Ubuntu中安装指定版本的Node.js&#xff0c;可以使用Node Version Manager (NVM)。以下是安装步骤&#xff1a; 首先&#xff0c;安装NVM。在命令行中输入&#xff1a; curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash 这个命令会下载并…

【附教程】2024,人工智能+声音,看这里就够了~16款AI音乐/音频/音效,声音克隆等ai软件与工具大合集~

AI音乐音频领域的技术正在迅速发展&#xff0c;为音乐创作和编辑带来了革命性的改变。这些技术通过深度学习和生成式模型&#xff0c;能够理解并模仿音乐的复杂结构和情感&#xff0c;从而创作出高质量的音乐作品。 AI音乐音频技术使得音乐创作变得更加高效和便捷。创作者只需…

杨辉三角(C语言)

杨辉三角 一.什么是杨辉三角 一.什么是杨辉三角 每个数等于它上方两数之和。 每行数字左右对称&#xff0c;由1开始逐渐变大。 第n行的数字有n项。 前n行共[(1n)n]/2 个数。 … 当前行的数上一行的数上一行的前一列的数 void yanghuisanjian(int arr[][20], int n) {for (int i…

blender怎么导入stl格式文件?

stl格式&#xff0c;一般是用来3D打印用的文件&#xff0c;这种模型一般很小&#xff0c;经常做来做一些DIY的配件&#xff0c;如下图&#xff0c;一共有七八个模型&#xff0c;3D打印机把每个模型实体打出来后&#xff0c;就可以给小朋友组装当智益玩具玩了&#xff0c;我们把…

【Node.js从基础到高级运用】二、搭建开发环境

Node.js入门&#xff1a;搭建开发环境 在上一篇文章中&#xff0c;我们介绍了Node.js的基础概念。现在&#xff0c;我们将进入一个更实际的阶段——搭建Node.js的开发环境。这是每个Node.js开发者旅程中的第一步。接下来&#xff0c;我们将详细讨论如何安装Node.js和npm&#…

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——03.差异表达基因筛选(2)

内容如下&#xff1a; 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的…

力扣199. 二叉树的右视图(DFS,BFS)

Problem: 199. 二叉树的右视图 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 无论是DFS还是BFS我们都要思考到达二叉树的每一层&#xff08;或者每一层中的每一个节点&#xff09;时&#xff0c;我们都该如何按题目要求做出对应得处理!!!在本体中我们主要是&#x…

Solidity攻击合约:重入攻击与危害分析

以太坊智能合约开发中&#xff0c;重入攻击是一种常见的安全漏洞。这种攻击通常发生在合约的递归调用中&#xff0c;攻击者通过构造恶意交易&#xff0c;使得原本合约在执行过程中不断调用自身或其他合约&#xff0c;从而耗尽合约的Gas&#xff08;交易费用&#xff09;&#x…

Oracle LeetCode 高频 SQL 50 题(进阶版)

https://leetcode.cn/studyplan/sql-premium-50/ 一、查询 1821. 寻找今年具有正收入的客户 selectcustomer_id from Customers where year 2021 group by customer_id having sum(revenue) > 0183. 从不订购的客户 select c.name as Customers from Customers c left j…