目录
1.工程搭建部署
方案一:完整工程导入
方案二:从零开始搭建
1.工程与module创建
2.数据库导入
3.项目启动
3.1 启动并访问user-service
3.2 启动并访问order-service
4.服务远程调用
时序图说明
服务远程调用实现
注入RestTemplate
RestTemplate完成远程服务调用
重启并访问order-service
RestTemplate如何实现远程服务调用
5.总结
6.推荐阅读资料
1.工程搭建部署
方案一:完整工程导入
-
cloud.zip,如无法运行尝试换未编译版:cloud-demo.zip
-
下载解压上述工程,ide工具导入
方案二:从零开始搭建
1.工程与module创建
1.1 父工程创建
1.2 子module创建
-
module名称:order-service、user-service
-
无效文件夹删除,整体结构如图2
1.3 父pom资源引入
粘贴以下资源依赖,粘贴后maven会自动拉取依赖,如未拉取请手动刷新
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR10</spring-cloud.version>
<mysql.version>5.1.47</mysql.version>
<mybatis.version>2.1.1</mybatis.version>
<lombok.version>1.18.20</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- springCloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
1.4 子module资源引入
user-service
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
order-service
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
1.5 业务代码编写
1.user-service
application.yml配置文件
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
username: root
password: root123456
driver-class-name: com.mysql.jdbc.Driver
mybatis:
type-aliases-package: cn.itcast.user.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
cn.itcast: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
|--mapper |-- UserMapper
package cn.itcast.user.mapper;
import cn.itcast.user.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* 用户持久层
*
* @author
* @date 2022-12-22 14:12
*/
public interface UserMapper {
/**
* 根据ID查找用户
* @param id 用户ID
* @return 用户实体信息
*/
@Select("select * from tb_user where id=#{id}")
User findById(@Param("id") Long id);
}
|--pojo |-- User
package cn.itcast.user.pojo;
import lombok.Data;
/**
* 用户实体
*
* @author
* @date 2022-12-22 14:07
*/
@Data
public class User {
/**
* 主键ID
*/
private Long id;
/**
* 用户姓名
*/
private String username;
/**
* 用户地址
*/
private String address;
}
|--service |-- UserService
package cn.itcast.user.service;
import cn.itcast.user.mapper.UserMapper;
import cn.itcast.user.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* 用户业务层
*
* @author
* @date 2022-12-22 14:13
*/
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
/**
* 根据ID查找用户
* @param id 用户ID
* @return 用户实体信息
*/
public User findById(Long id) {
if (Objects.isNull(id)) {
return null;
}
return userMapper.findById(id);
}
}
|--web |-- UserController
package cn.itcast.user.web;
import cn.itcast.user.pojo.User;
import cn.itcast.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 用户控制层
*
* @author
* @date 2022-12-22 14:15
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 根据ID查找用户
* @param id ID
* @return 用户信息
*/
@GetMapping("/{id}")
public User findById(@PathVariable("id") Long id) {
return userService.findById(id);
}
}
|--UserApplication
package cn.itcast.user;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 启动类
*
* @author
* @date 2022-12-22 14:00
*/
@MapperScan("cn.itcast.user.mapper")
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
System.out.println("用户工程启动成功");
}
}
2.order-service
application.yml配置文件
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
username: root
password: root123456
driver-class-name: com.mysql.jdbc.Driver
mybatis:
type-aliases-package: cn.itcast.order.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
cn.itcast: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
|--mapper |-- OrderMapper
package cn.itcast.order.mapper;
import cn.itcast.order.pojo.Order;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* 订单持久层
*
* @author
* @date 2022-12-22 14:22
*/
public interface OrderMapper {
/**
* 根据ID查找订单
* @param id 订单ID
* @return 订单对象
*/
@Select("select * from tb_order where id=#{id}")
Order findById(@Param("id")Long id);
}
|--pojo |-- Order
package cn.itcast.order.pojo;
import lombok.Data;
/**
* 订单实体(vo并非dto)
*
* @author
* @date 2022-12-22 14:19
*/
@Data
public class Order {
/**
* 主键ID
*/
private Long id;
/**
* 用户主键ID
*/
private Long userId;
/**
* 商品名称
*/
private String name;
/**
* 商品价格
*/
private Long price;
/**
* 商品数量
*/
private Integer num;
/**
* 用户信息
*/
private User user;
}
|-- User
package cn.itcast.order.pojo;
import lombok.Data;
/**
* 用户实体
*
* @author
* @date 2022-12-22 14:07
*/
@Data
public class User {
/**
* 主键ID
*/
private Long id;
/**
* 用户姓名
*/
private String username;
/**
* 用户地址
*/
private String address;
}
|--service |-- OrderService
package cn.itcast.order.service;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 订单业务层
*
* @author
* @date 2022-12-22 14:24
*/
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
/**
* 根据ID查找订单
* @param id 订单ID
* @return 订单对象
*/
public Order findById(Long id) {
Order order = orderMapper.findById(id);
return order;
}
}
|--web |-- OrderController
package cn.itcast.order.web;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 订单控制层
*
* @author
* @date 2022-12-22 14:25
*/
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* 根据ID查找订单
* @param orderId 订单ID
* @return 订单对象
*/
@GetMapping("/{orderId}")
public Order findById(@PathVariable("orderId")Long orderId) {
return orderService.findById(orderId);
}
}
|--OrderApplication
package cn.itcast.order;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 启动类
*
* @author
* @date 2022-12-22 14:05
*/
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
System.out.println("订单工程启动成功");
}
}
2.数据库导入
-
需参见上图新建两个数据库,然后导入DDL语句
-
cloud-order.sql
-
cloud-user.sql
导完如下
3.项目启动
3.1 启动并访问user-service
3.2 启动并访问order-service
观察发现,虽然order-service服务调用成功,但是里面的user对象却是空的。原因我们应该也可以了解到是因为User对象数据数存储在数据库:tb_user,而此工程连接数据库是:tb_order,查询此数据库时无法获取对应的User数据,怎么获取?获取的具体实现我们将在下一章节进一步铺开。
4.服务远程调用
分析现有链路调用关系可以发现:
-
http://localhost:8081/user/{id} 可以获取用户信息
-
http://localhost:8081/order/{orderId} 可以获取订单信息,但是用户信息为空
想要订单信息中返回用户信息,只要在获取订单链路中追加对用户信息的获取、返回值的组装即可,由此引申出微服务之间的远程服务调用,具体调整可见下图。
时序图说明
上述图形为时序图,一般用来描述系统与系统之间的交互流程,主要是交互API、代码顺序、参数一般忽略,时序图不同于业务流程,更关注业务实现过程中系统前后依赖,数据请求与返回,以下为笔者实际工作场景示例:
服务远程调用实现
注入RestTemplate
此处推荐一个小的优雅工具:Carbon | Create and share beautiful images of your source code
RestTemplate完成远程服务调用
在此笔者除了完成远程服务调用,同时对代码做了结构化、异常校验、函数封装。虽然此处逻辑并不复杂,但是对于主干逻辑简化和代码风格,希望能起到一个引导作用。
package cn.itcast.order.service;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.Objects;
/**
* 订单业务层
*
* @author
* @date 2022-12-22 14:24
*/
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
/**
* 根据ID查找订单
* @param id 订单ID
* @return 订单对象
*/
public Order findById(Long id) {
// step 1 : 查询订单原始数据
Order order = orderMapper.findById(id);
if (Objects.isNull(order)) {
return null;
}
// step 2 : 查询用户数据
User user = queryUserInfoById(order.getUserId());
// step 3 : 组装用户数据
if (Objects.nonNull(user)) {
order.setUser(user);
}
// step 4: 数据返回
return order;
}
/**
* 根据用户ID查找用户信息
* @param userId 用户ID
* @return 用户信息
*/
private User queryUserInfoById(Long userId) {
String url = "http://localhost:8081/user/" + userId;
return restTemplate.getForObject(url, User.class);
}
}
重启并访问order-service
RestTemplate如何实现远程服务调用
5.总结
本节笔者带领大家完成了SpringCloud工程从0->1的搭建,当然你不想搭建也可以直接采用方案一,二者等效,至此读者们完成了一个微服务工程的搭建、部署、访问。同时在本节最后一章,笔者基于RestTemplate发起的http请求实现远程调用,实现当A系统想要获取B系统数据时的跨系统数据交互。然而RESTful API(进一步了解可移步:链接)访问并不是微服务的唯一解决方案,如Dubbo的交互一样可以实现,希望读者们能不限于此。
在本章节,笔者希望大家对于微服务的拆分,服务调用关系有个初步认知,本例中当order-service调用user-service时,前者就充当了服务调用方,后者则是服务提供方,这种调用关系在后续我们将会大量使用。
思考问题
-
如何实现服务远程调用?
-
服务远程调用解决了什么问题?
-
还有哪些技术可以实现远程服务调用?
6.推荐阅读资料
-
了解时序图绘制:https://www.cnblogs.com/liuyitan/p/16631240.html