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
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 port8081 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-casetrue 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 * from tb_user where id=#{id}") ( User findById( ("id") Long id); }
|--pojo
|-- User
package cn.itcast.user.pojo; import lombok.Data; /** * 用户实体 * * @author * @date 2022-12-22 14:07 */ 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 */ public class UserService { 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 */ "/user") (public class UserController { private UserService userService; /** * 根据ID查找用户 * @param id ID * @return 用户信息 */ "/{id}") ( public User findById( ("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 */ "cn.itcast.user.mapper") ( public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); System.out.println("用户工程启动成功"); } }
2.order-service
application.yml配置文件
server port8080 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-casetrue 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 * from tb_order where id=#{id}") ( Order findById( ("id")Long id); }
|--pojo
|-- Order
package cn.itcast.order.pojo; import lombok.Data; /** * 订单实体(vo并非dto) * * @author * @date 2022-12-22 14:19 */ 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 */ 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 */ public class OrderService { 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 */ "/order") (public class OrderController { private OrderService orderService; /** * 根据ID查找订单 * @param orderId 订单ID * @return 订单对象 */ "/{orderId}") ( public Order findById( ("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 */ "cn.itcast.order.mapper") ( 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
此处推荐一个小的优雅工具:https://carbon.now.sh/
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 */ public class OrderService { private OrderMapper orderMapper; 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如何实现远程服务调用