
Spring Cloud 是基于 Spring Boot 进行开发的,Spring Boot 3.X 以下的版本,Spring 官方已不再进行维护(还可以继续使用),Spring Boot 3.X 的版本,使用的 JDK 版本基线为 JDK17。
在使用 Spring Cloud Azure 的场景(即 Spring Cloud 在 Azure 上对 MySQL 的支持)中,官方明确表示其 "当前版本" 支持 MySQL 5.7 或 8.0。
对于 MySQL JDBC 驱动 MySQL Connector/J 的官方文档显示:
实现一个电商平台。一个电商平台包含的内容非常多,以京东为例,仅从首页上就可以看到巨多的功能。

我们该如何实现呢?如果把这些功能全部写在一个服务里,这个服务将是巨大的。巨多的会员,巨大的流量,微服务架构是最好的选择。
而微服务应用开发的第一步,就是服务拆分。拆分后才能进行 "各自开发"。
微服务到底多小才算 "微",这个在业界并没有明确的标准。微服务并不是越小越好,服务越小,微服务架构的优点和缺点都会越来越明显。
服务越小,微服务的独立性就会越来越高,但同时,微服务的数量也会越多,管理这些微服务的难度也会提高。所以服务拆分也要考虑场景。
还是以企业管理为例:
企业中一个员工的工作内容与企业规模,项目规模等都有关系。
在小公司,一个员工可能需要负责很多部门的事情,大公司的话,一个部门的工作可能需要多个员工来处理。
拆分微服务一般遵循如下原则:
单一职责原则原本是面向对象设计中的一个基本原则,它指的是一个类应该专注于单一功能。不要存在多于一个导致类变更的原因。
在微服务架构中,一个微服务只负责一个功能或业务领域,每个服务应该有清晰的定义和边界,只关注自己的特定业务领域。
组织团队也是,一个人专注做一件事情的效率远高于同时关注多件事情。 比如一个人同时管理和维护一份代码,要比多个人同时维护多份代码的效率高。 比如电商系统:

服务自治是指每个微服务都应该具备高度自治的能力,即每个服务要能做到独立开发、独立测试、独立构建、独立部署、独立运行。
以上面的电商系统为例,每一个微服务应该有自己的存储、配置,在进行开发、构建、部署、运行和测试时,并不需要过多关注其他微服务的状态和数据。
比如企业管理: 每个部分负责每个部门的事情,并且尽可能少的受其他团队影响。 研发部门只负责需求功能的开发,而不负责需求文档的书写和 UI 的设计。并且其他部门的人员变动,流程变更,也尽可能少的影响研发部门。部门和部门之间尽可能自治。

微服务之间需要做到单向依赖,严禁循环依赖,双向依赖!

如果一些场景确实无法避免循环依赖或者双向依赖,可以考虑使用消息队列等其他方式来实现~
一个完整的电商系统是庞大的,这里重点关注如何使用 Spring Cloud 解决微服务架构中遇到的问题。
以订单列表为例:

简单来看,这个页面提供了以下信息:
根据服务的单一职责原则,我们把服务进行拆分为:订单服务、商品服务

根据服务自治原则,每个服务都应有自己独立的数据库!
订单服务:
-- 建库
create database if not exists cloud_order charset utf8mb4;
use cloud_order;
-- 订单表
DROP TABLE IF EXISTS order_detail;
CREATE TABLE order_detail (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '订单id',
`user_id` BIGINT(20) NOT NULL COMMENT '用户ID',
`product_id` BIGINT(20) NULL COMMENT '产品id',
`num` INT(10) NULL DEFAULT 0 COMMENT '下单数量',
`price` BIGINT(20) NOT NULL COMMENT '实付款',
`delete_flag` TINYINT(4) NULL DEFAULT 0,
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now(),
PRIMARY KEY (id)
) ENGINE = INNODB DEFAULT CHARACTER SET = utf8mb4 COMMENT = '订单表';
-- 数据初始化
insert into order_detail (user_id,product_id,num,price) values
(2001, 1001,1,99),
(2002, 1002,1,30),
(2001, 1003,1,40),
(2003, 1004,3,58),
(2004, 1005,7,85),
(2005, 1006,7,94);商品服务:
create database if not exists cloud_product charset utf8mb4;
use cloud_product;
-- 产品表
DROP TABLE IF EXISTS product_detail;
CREATE TABLE product_detail (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '产品id',
`product_name` varchar(128) NULL COMMENT '产品名称',
`product_price` BIGINT(20) NOT NULL COMMENT '产品价格',
`state` TINYINT(4) NULL DEFAULT 0 COMMENT '产品状态 0-有效 1-下架',
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now(),
PRIMARY KEY (id)
) ENGINE = INNODB DEFAULT CHARACTER SET = utf8mb4 COMMENT = '产品表';
-- 数据初始化
insert into product_detail (id, product_name,product_price,state) values
(1001,"T恤", 101, 0),
(1002, "短袖",30, 0),
(1003, "短裤",44, 0),
(1004, "卫衣",58, 0),
(1005, "马甲",98, 0),
(1006,"羽绒服", 101, 0),
(1007, "冲锋衣",30, 0),
(1008, "袜子",44, 0),
(1009, "鞋子",58, 0),
(10010, "毛衣",98, 0)pom.xml 文件:
pom.xml 文件:properties 来进行版本号的统一管理dependencyManagement 来管理依赖pom<?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.liren</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.6</version>
<relativePath/> <!-- 从远程仓库下载拉取,而不在本地找父POM -->
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>17</java.version>
<mybatis.version>3.0.3</mybatis.version>
<mysql.version>8.0.33</mysql.version>
<spring-cloud.version>2022.0.3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>${mybatis.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
然后声明项目依赖和项目构建插件:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>创建商品服务 product-service 的过程和上面是一样的!
然后声明项目依赖和项目构建插件:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}server:
port: 8080
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/cloud_order?characterEncoding=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
# 配置打印 MyBatis 日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true #配置驼峰自动转换@Data
public class OrderInfo {
private Integer id;
private Integer userId;
private Integer productId;
private Integer num;
private Integer price;
private Integer deleteFlag;
private Date createTime;
private Date updateTime;
}@RequestMapping("/order")
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@RequestMapping("/{orderId}")
public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId){
return orderService.selectOrderById(orderId);
}
}@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
return orderInfo;
}
}@Mapper
public interface OrderMapper {
@Select("select * from order_detail where id=#{orderId}")
OrderInfo selectOrderById(Integer orderId);
}访问 url:http://127.0.0.1:8080/order/1
页面正常返回结果:

@SpringBootApplication
public class ProductServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServiceApplication.class, args);
}
}server:
port: 9090 # 后面需要多个服务一起启动,所以设置为不同的端口号
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/cloud_product?characterEncoding=utf8&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration: # 配置打印 MyBatis 日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true #配置驼峰自动转换@Data
public class ProductInfo {
private Integer id;
private String productName;
private Integer productPrice;
private Integer state;
private Date createTime;
private Date updateTime;
}@RequestMapping("/product")
@RestController
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer productId){
System.out.println("收到请求,Id:"+productId);
return productService.selectProductById(productId);
}
}@Service
public class ProductService {
@Autowired
private ProductMapper productMapper;
public ProductInfo selectProductById(Integer id){
return productMapper.selectProductById(id);
}
}@Mapper
public interface ProductMapper {
@Select("select * from product_detail where id=#{id}")
ProductInfo selectProductById(Integer id);
}访问 url:http://127.0.0.1:9090/product/1001
页面正常返回结果:

根据订单查询订单信息时,根据订单中的产品 ID,获取产品的详细信息。

实现思路:order-service 服务向 product-service 服务发送一个 http 请求,把得到的返回结果,和订单结果融合在一起,返回给调用方。
实现方式:采用 Spring 提供的 RestTemplate。
实现 http 请求的方式,有很多,可参考:https://zhuanlan.zhihu.com/p/670101467
@Configuration
public class BeanConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}ProductInfo 复制到订单模块中,然后修改 OrderInfo(这是错误示范,这里只是为了方便演示)
order-service 中的 OrderService@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}访问 url:http://127.0.0.1:8080/order/1
页面返回结果:

RestTemplate 是 Spring 提供的一个同步 HTTP 客户端,用来在 Java 程序中发送 HTTP 请求,调用远程 REST 接口。
简单说,它是:
常用方法如下所示:
方法名 | 功能 | 类似于 |
|---|---|---|
getForObject() | 发送 GET 请求并获取响应体 | axios.get / fetch |
getForEntity() | 获取完整响应(含 headers、status) | 更底层 |
postForObject() | 发送 POST 请求 | axios.post |
put() | PUT 请求 | axios.put |
delete() | DELETE 请求 | axios.delete |
exchange() | 高级通用方法,可自定义 header、method | 通用 API |
不过虽然它是 "调用远程接口" 的利器,但在 Spring Cloud 中,通常会被 Feign 进一步封装替代。下面是不同的方案的区别:
特性 | RestTemplate | WebClient | Feign |
|---|---|---|---|
调用方式 | 同步阻塞 | 异步非阻塞 | 声明式接口 |
复杂度 | 简单 | 稍复杂 | 最简单(写接口就行) |
Spring Cloud 集成 | ✅ 有 | ✅ 有 | ✅ 强烈推荐 |
推荐场景 | 普通 HTTP 调用 | 高并发异步调用 | 微服务间调用 |
RESTful API(Representational State Transfer API)是一种基于 HTTP 协议、面向资源的接口设计风格。
简单说:
假如系统中有个 "用户模块":
传统的接口可能会写成:
/getUserList
/getUserById
/createUser
/updateUser
/deleteUser而 RESTful 风格 认为这些动作其实都是针对一个 "资源(Resource)" —— 比如 "用户"。所以 RESTful 把接口 "资源化":
操作 | URL(资源路径) | HTTP 方法 |
|---|---|---|
获取所有用户 | /users | GET |
获取单个用户 | /users/{id} | GET |
创建用户 | /users | POST |
修改用户 | /users/{id} | PUT |
删除用户 | /users/{id} | DELETE |
是不是瞬间感觉清爽多了?没有那些 "动词堆叠" 的接口名,全靠 HTTP 方法表达语义。
原则 | 含义 |
|---|---|
1️⃣ 以资源为中心(Resource Oriented) | 接口操作的是“资源”,而不是“动作” |
2️⃣ 使用标准 HTTP 方法 | GET、POST、PUT、DELETE 等 |
3️⃣ 通过 URL 标识资源 | 每个资源都有唯一 URI |
4️⃣ 使用 JSON 表达资源状态 | 客户端与服务端交换 JSON |
5️⃣ 无状态(Stateless) | 每次请求都独立,不依赖上一次 |
6️⃣ 统一响应格式 | 一般定义通用结构 {code, msg, data} |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。