文章目录
- 数据准备
- 工程搭建
-
- 构建父子工程
-
- 创建父工程
-
- [DependencyManagement 和 Dependencies](#DependencyManagement 和 Dependencies)
- [Spring Cloud 版本](#Spring Cloud 版本)
- 创建子项目---订单服务
- 创建子项目---商品服务
- 完善订单服务
- 完善商品服务
- 远程调用
数据准备
根据服务自洽原则,每个服务都应有自己独立的数据库
sql
-- 创建数据库
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);
sql
-- 创建数据库
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, 1),
(1005, '耳甲', 90, 1),
(1006, '羽绒服', 100, 0),
(1007, '裤子', 31, 0),
(1008, '鞋子', 44, 0),
(1009, '卫衣', 58, 0),
(1010, '毛衣', 98, 0);
工程搭建
构建父子工程
创建父工程
-
创建一个空的
Maven
项目,删除所有代码,只保留pom.xml
-
完善
pom
文件
使用 properties
来进行版本号的统一管理,使用 dependencyManagement
来管理依赖,声明父工程的打包方式为 pom
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>250709-spring-cloud-demo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.6</version>
<relativePath> <!-- lookup parent from repository --> </relativePath>
</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>
</dependencies>
</dependencyManagement>
</project>
DependencyManagement 和 Dependencies
dependencies
:将所依赖的jar
直接加到项目中。子项目也会继承该依赖dependencyManagement
:只是声明依赖,并不实现jar
包引入。- 如果子项目需要用到相关依赖,需要显示声明
- 如果子项目没有指定具体版本,会从父项目中读取
version
- 如果子项目中制定了具体版本,就会使用子项目中指定的
jar
版本。 - 此外父工程的打包方式应该是
pom
,不是jar
,这里需要手动使用packaging
来声明
SpringBoot
实现依赖jar
包版本的管理,也是这种方式
依赖 jar
的版本判断
xml
<dependencies>
<dependency> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency></dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
- 上述代码中,
lombok
会被直接引入到当前项目以及子项目中,mysql-connector-j
不会实际引入jar
,子项目继承时必须显示声明
Spring Cloud 版本
SpringCloud
是基于 SpringBoot
搭建的,所以 SpringCloud
版本与 SpringBoot
版本有关
- 我们项目中使用的
SpringBoot
版本为3.1.6
,对应的SpringCloud
版本应该为2022.0.x
,选择任意就可以
创建子项目---订单服务


声明项目依赖和项目构建插件
xml
<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>
创建子项目---商品服务

声明项目依赖和项目构建插件
xml
<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>
完善订单服务
完善启动类,配置文件
启动类
java
package org.example.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
配置文件
yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://127.0.0.1/cloud_order?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 20230153018
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 # 配置驼峰自动转换
业务代码
- 实体类
java
package org.example.order.model;
import lombok.Data;
import java.util.Date;
@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;
}
controller
java
package org.example.order.controller;
import org.example.order.model.OrderInfo;
import org.example.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@RequestMapping("/{orderId}")
public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId) {
return orderService.selectOrderById(orderId);
}
}
Service
java
package org.example.order.service;
import org.example.order.mapper.OrderMapper;
import org.example.order.model.OrderInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
return orderInfo;
}
}
Mapper
java
package org.example.order.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.example.order.model.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
页面正常返回结果:
完善商品服务
完善启动类,配置文件
启动类
java
package org.example.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProductServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServiceApplication.class, args);
}
}
配置文件
yml
server:
port: 9090
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/cloud_product?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 20230153018
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 # 配置驼峰自动转换
- 后面需要多个服务一起启动,所以设置为不同的端口号
业务代码
- 实体类
java
package org.example.order.model;
import lombok.Data;
import java.util.Date;
@Data
public class ProductInfo {
private Integer id;
private String productName;
private Integer productPrice;
private Integer state;
private Date createTime;
private Date updateTime;
}
Controller
java
package org.example.order.controller;
import org.example.order.model.ProductInfo;
import org.example.order.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/product")
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
java
package org.example.order.service;
import org.example.order.mapper.ProductMapper;
import org.example.order.model.ProductInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Autowired
private ProductMapper productMapper;
public ProductInfo selectProductById(Integer id) {
return productMapper.selectProductById(id);
}
}
Mapper
java
package org.example.order.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.example.order.model.ProductInfo;
@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
- 定义
RestTemplate
java
package org.example.order.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class BeanConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
- 将
ProductInfo
添加到order_service.model
里面,并将OrderInfo
改为
java
package org.example.order.model;
import lombok.Data;
import java.util.Date;
@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;
private ProductInfo productInfo;
}
- 修改
order_service
中的OrderService
java
package org.example.order.service;
import org.example.order.mapper.OrderMapper;
import org.example.order.model.OrderInfo;
import org.example.order.model.ProductInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@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;
}
}
RestTemplate
详细使用可参考: https://www.cnblogs.com/54chensongxia/p/11414923.html
测试 :
访问 URL: http://127.0.0.1:8080/order/1
页面返回结果:
- 注意:两个启动类都要启动