第32周Java微服务入门 Spring开发课程查询功能

Spring Cloud课程项目笔记

一、课程项目整体介绍

1.1 项目概述

  • 项目名称: Spring Cloud课程查询项目
  • 项目规模: 相对较小,但涵盖Spring Cloud所有重要组件
  • 学习目标: 掌握Spring Cloud用法、特点及组件间关系

1.2 项目内容

  • Spring Cloud简介: 基本概念与用途
  • 课程查询项目实践: 实际项目开发,包括模块间调用、断路器、网关等核心内容
  • 核心思想: 通过项目实践掌握Spring Cloud的核心思想

1.3 项目价值

  • 技术细节: 关注Spring Cloud的技术细节
  • 实践应用: 通过实践项目加深理解和应用

二、项目整体设计技术

2.1 设计模块

  • 数据流向: 明确数据在系统中的流动路径
  • 表设计: 数据库表结构设计
  • 项目结构: 完成设计后明确项目的大体结构

三、具体模块开发

3.1 开发流程

  • 模块化开发: 使用不同模块实现不同功能,模块间高度独立
  • 开发起点: 从课程列表模块开始

3.2 课程列表模块

  • 功能: 提供课程列表信息
  • 技术: 涉及web项目开发知识

四、课程价格模块开发

4.1 模块重点

  • 功能: 课程价格查询和管理
  • 接口调用: 需要调用课程列表模块的接口

4.2 服务注册与发现

  • 组件: 使用Eureka组件
  • 作用: 服务之间能够轻易发现对方
  • 调用方式: 配置服务调用,应对地址变化

4.3 负载均衡

  • 算法: 不同时段、IP、随机分配等
  • 工具: 使用Ribbon实现负载均衡算法

4.4 断路器

  • 作用: 服务不可用时返回默认响应,保证用户体验
  • 实现: 使用Hystrix实现断路功能

4.5 网关

  • 作用: 统一通信,优化管理
  • 组件: Spring Cloud Netflix Zuul
  • 功能: 提供统一入口,增强安全性,支持过滤器

五、Spring Cloud组件介绍与项目结合

5.1 组件介绍

  • 服务发现与注册: Eureka Server和Eureka Client
  • 配置管理: 集中管理配置信息
  • 服务调用: 支持多种通信协议和负载均衡
  • 熔断与降级: 保障系统稳定性和可用性

5.2 项目结合

  • 组件开发: 完成组件开发后项目随之完成
  • 整体测试: 测试各模块内容、模块间调用、熔断等

六、项目重难点总结

6.1 项目能力

  • 开发能力: 掌握Spring Cloud开发项目的能力
  • 实践应用: 通过课程查询案例具备实际开发能力

七、Spring Cloud简介

7.1 定位

  • 框架定位: 提供多样化工具快速构建分布式系统

7.2 成长路径

  • 发展路径: Spring → Spring Boot → Spring Cloud

7.3 家族组件

  • 组件丰富: 根据业务需求提供对应组件

八、核心组件

8.1 服务注册中心

  • 组件: Spring Cloud Netflix Eureka
  • 作用: 服务自动注册与发现

8.2 服务调用方式

  • 推荐方式: 使用HTTP
  • RESTAPI: 采用Restful风格API
  • 工具: 使用Feign和Ribbon

8.3 服务网关

  • 组件: Spring Cloud Netflix Zuul
  • 作用: 提供统一入口,增强安全性
  • 功能: 支持过滤器,处理每个请求

8.4 断路器

  • 组件: Hystrix
  • 作用: 保证服务在高流量下正常运转

九、知识小结

知识点 核心内容 考试重点/易混淆点 难度系数
Spring Cloud简介 Spring Cloud的基本概念、定位、成长路径和家族组件 Spring Cloud的定位和成长路径
项目实践 通过课程查询项目学习Spring Cloud组件,包括模块间调用、断路器、网关等 Spring Cloud核心思想和组件应用
服务注册与发现 使用Eureka实现服务的自动注册与发现 Eureka Server和Client的配合使用
负载均衡 使用Ribbon实现不同的负载均衡算法 负载均衡算法的选择和应用
断路器 使用Hystrix实现断路功能,保证服务稳定性 断路器的工作原理和实现
服务网关 使用Zuul实现统一入口和安全管理 网关的作用和过滤器的使用

Spring Cloud项目整体设计与实现笔记

一、项目整体设计

1.1 项目介绍

  • 项目目标: 实现一个最小力度的慕课网课程查询项目。
  • 主要模块:
    • 课程列表模块: 提供课程列表信息。
    • 课程价格模块: 提供课程价格信息。

1.2 接口设计

  • 课程列表模块接口:
    • 功能: 从数据库读取课程列表,处理并返回。
  • 课程价格模块接口:
    • 单个课程价格接口: 接收课程ID,返回课程价格。
    • 整合课程列表和价格的接口:
      • 难点: 通过远程调用获取课程列表,涉及远程调用和数据处理。
      • 过程: 获取课程列表后,遍历每个课程,调用单个课程价格接口补充价格信息,最终返回完整列表。

1.3 系统数据流向

  1. 课程列表数据: 存储在数据库中,由课程列表服务读取并展示。
  2. 课程价格数据: 由课程价格服务获取并读取。
  3. 服务交互: 课程列表服务与课程价格服务交互,整合数据后对外展示。

1.4 表设计

  • course表(课程列表):
    • 字段: ID(主键)、course_id(课程ID)、course_name(课程名)、valid(上架状态)。
    • 特点: valid字段用于筛选上架课程。
  • 课程价格表:
    • 字段: ID、course_id(课程ID)、price(价格)。
    • 关联: 通过course_id与course表关联。

二、新建项目

2.1 选择新建项目

  • 工具: 使用IntelliJ IDEA。
  • 项目类型: Spring Initializr,适用于Maven多模块项目。

2.2 填写项目基本信息

  • Group: 项目组名(如com.mukewang)。
  • Artifact: 项目artifact名(如spring-cloud-course)。
  • 配置: JAVA 8和Maven构建方式。

2.3 选择项目依赖

  • 依赖版本: 选择2.1.14或2.1版本。
  • 策略: 统一选择一个版本,后续根据需要更改。

2.4 确定项目文件夹

  • 位置选择: 用户自定义文件夹位置。
  • 自动导入依赖: 启用自动导入,避免手动操作。

2.5 查看项目初始化进度

  • 初始化状态: 通过进度条查看。
  • 影响因素: 网速、机器性能、包下载时间。
  • 完成表现: 左侧显示代码和测试文件夹,文件夹颜色区分(蓝色为代码,绿色为测试)。

2.6 删除第一层的src

  • 操作: 删除第一层的src文件夹。
  • 后续步骤: 在项目根目录右键选择新建子模块。

2.7 新建子模块

  1. 删除子模块的src:
    • 步骤: 新建子模块后删除其src文件夹。
    • 结果: 子模块继承父模块配置。
  2. 新建子模块课程列表:
    • 选择: 确保父模块为正确层级。
    • 结果: 创建course-list模块,用于存放业务代码。
  3. 新建子模块课程价格:
    • 步骤: 同样选择正确父模块。
    • 结果: 创建course-price模块。

2.8 查看POM文件

  • 最外层POM: 包含所有子模块。
  • 子模块POM: 自动更新,显示在父模块的POM文件中。

三、知识小结

知识点 核心内容 考试重点/易混淆点 难度系数
项目设计 项目整体设计包括项目介绍、接口设计、数据流向和表设计 数据流向和表设计的具体实现
接口设计 课程列表和价格模块的接口设计,特别是整合接口的远程调用和数据处理 远程调用的实现和数据整合的难点
表设计 course表和课程价格表的结构及关联关系 表之间的关联和valid字段的使用
新建项目 使用Spring Initializr创建多模块项目,配置基本信息和依赖 项目初始化进度和文件夹结构的确认
子模块创建 删除src文件夹,新建子模块并确认层级结构 子模块的继承关系和POM文件的自动更新
# Spring Cloud课程列表与价格模块开发笔记

## 一、课程列表模块开发

### 1.1 新建包
- **位置**: `src/main/java`
- **包名**: `com.Imock.course`

### 1.2 写主启动类
- **类名**: `CourseListApplication`
- **注解**: `@SpringBootApplication`
- **主函数**:
  ```java
  public static void main(String[] args) {
      SpringApplication.run(CourseListApplication.class, args);
  }

1.3 引入依赖

  • Spring Boot依赖:
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
  • My相关依赖:
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
    dependency>
    <dependency>
        <artifactId>mybatis-spring-boot-starterartifactId>
        <version>2.1.1version>
    dependency>
    
  • Spring Boot Maven插件:
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>
    

1.4 配置文件

  • 文件名: application.properties
  • 内容:
    server.port=8081
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/course_practice
    spring.datasource.username=root
    spring.datasource.password=12345678
    spring.application.name=course-list
    logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %msg%n
    

1.5 完善Controller层

  • Controller类: CourseListController
  • 注解: @RestController
  • 方法:
    @GetMapping("/courses")
    public List<Course> crossList() {
        return courseListService.getCourseList();
    }
    

1.6 完善Service层

  • 接口: CourseListService
    public interface CourseListService {
        List<Course> getCourseList();
    }
    
  • 实现类: CourseListServiceImpl
    @Service
    public class CourseListServiceImpl implements CourseListService {
        @Autowired
        private CourseMapper courseMapper;
    
        @Override
        public List<Course> getCourseList() {
            return courseMapper.findValidCourses();
        }
    }
    

1.7 完善Mapper层

  • Mapper接口: CourseMapper
    @Mapper
    @Repository
    public interface CourseMapper {
        @Select("SELECT * FROM course WHERE valid = 1")
        List<Course> findValidCourses();
    }
    

1.8 实体类

  • 实体类: Course
    public class Course implements Serializable {
        private Integer id;
        private Integer courseID;
        private String courseName;
        private Integer valid;
    
        // Getters and Setters
    }
    

二、常见错误排查

2.1 项目最外层pom.xml

  • 作用: 管理项目依赖和版本。
  • Spring Boot版本: 2.1.2.RELEASE

2.2 启动项目

  • 启动过程: 编译构建。
  • 测试方式: 浏览器或Postman访问接口。

2.3 访问接口返回404

  • 原因: 未配置对应URL。
  • 解决: 检查Controller中的URL配置。

2.4 实体类序列化问题

  • 错误: 实体类不能被序列化。
  • 解决: 实现Serializable接口。

2.5 字段匹配问题

  • 错误: 类字段缺少get和set方法。
  • 解决: 生成get和set方法。

三、课程价格模块开发

3.1 新建包

  • 包名: com.Imock.course

3.2 配置文件

  • 修改端口号: 8082
  • 应用名: course-price

3.3 课程价格控制器

  • Controller类: CoursePriceController
    @RestController
    public class CoursePriceController {
        @Autowired
        private CoursePriceService coursePriceService;
    
        @GetMapping("/price")
        public Integer getCoursePrice(@RequestParam Integer courseID) {
            CoursePrice coursePrice = coursePriceService.getCoursePrice(courseID);
            return coursePrice.getPrice();
        }
    }
    

3.4 课程价格服务

  • 接口: CoursePriceService
    public interface CoursePriceService {
        CoursePrice getCoursePrice(Integer courseID);
    }
    
  • 实现类: CoursePriceServiceImpl
    @Service
    public class CoursePriceServiceImpl implements CoursePriceService {
        @Autowired
        private CoursePriceMapper coursePriceMapper;
    
        @Override
        public CoursePrice getCoursePrice(Integer courseID) {
            return coursePriceMapper.findCoursePrice(courseID);
        }
    }
    

3.5 课程价格Mapper

  • Mapper接口: CoursePriceMapper
    @Mapper
    @Repository
    public interface CoursePriceMapper {
        @Select("SELECT * FROM course_price WHERE course_id = #{courseID}")
        CoursePrice findCoursePrice(@Param("courseID") Integer courseID);
    }
    

3.6 课程价格实体类

  • 实体类: CoursePrice
    public class CoursePrice implements Serializable {
        private Integer id;
        private Integer courseID;
        private Integer price;
    
        // Getters and Setters
    }
    

3.7 启动类

  • 类名: CoursePriceApplication
    @SpringBootApplication
    public class CoursePriceApplication {
        public static void main(String[] args) {
            SpringApplication.run(CoursePriceApplication.class, args);
        }
    }
    

Eureka服务注册与发现笔记

一、Eureka的作用

1.1 引入背景

  • 问题根源: 服务调用中IP地址写死,导致调整困难。
  • Eureka作用: 提供服务注册与发现,动态管理服务变化。

1.2 架构概述

  • 模块组成: Eureka架构分为Eureka Server、服务提供者和服务调用方。
  • 工作流程:
    1. 服务提供者: 注册服务信息到Eureka Server。
    2. 服务调用方: 从Eureka Server获取服务地址后进行调用。

二、引入Eureka

2.1 引入步骤

2.1.1 新建模块
  • 位置: 根项目中,与业务代码平级。
  • 命名: eureka-server,区别于Eureka Client。
2.1.2 添加依赖
  • 依赖内容:
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
    dependency>
    
  • 依赖管理: 在最外层pom文件中使用标签统一管理版本。
2.1.3 编写配置文件
  • 文件名: application.properties
  • 配置内容:
    server.port=8000
    spring.application.name=eureka-server
    eureka.instance.hostname=localhost
    eureka.client.register-with-eureka=false
    eureka.client.fetch-registry=false
    eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
    
2.1.4 编写代码
  • 启动类:
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaServerApplication {
        public static void main(String[] args) {
            SpringApplication.run(EurekaServerApplication.class, args);
        }
    }
    

2.2 启动与验证

  • 启动: 运行Eureka Server项目。
  • 验证: 浏览器访问 http://localhost:8000,查看Eureka管理界面。

三、Eureka Client改造

3.1 课程列表模块改造

3.1.1 添加依赖
  • 依赖内容:
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
    dependency>
    
3.1.2 配置文件修改
  • 文件: application.properties
  • 配置内容:
    eureka.client.service-url.defaultZone=http://localhost:8000/eureka/
    
3.1.3 启动模块
  • 启动: 运行课程列表模块。
  • 验证: 查看Eureka Server管理界面,确认服务注册成功。

3.2 课程价格模块改造

3.2.1 配置文件修改
  • 文件: application.properties
  • 配置内容:
    eureka.client.service-url.defaultZone=http://localhost:8000/eureka/
    
3.2.2 启动模块
  • 启动: 运行课程价格模块。
  • 验证: 查看Eureka Server管理界面,确认服务注册成功。

四、总结

4.1 Eureka Server

  • 作用: 服务注册与发现中心。
  • 配置: 端口、服务名、实例主机名等。
  • 启动: 成功后可通过管理界面查看服务状态。

4.2 Eureka Client

  • 改造步骤: 添加依赖、配置服务地址、启动模块。
  • 验证: 通过Eureka Server管理界面确认服务注册成功。

4.3 服务交互

  • 流程: 服务提供者注册到Eureka Server,服务调用方从Eureka Server获取地址后进行调用。

Spring Cloud服务间调用、负载均衡与断路器整合笔记

一、服务间调用

1.1 HTTP请求

  • 作用: 服务间调用的默认方式。
  • 特点: 参数指定繁琐,结果需要解析。
  • 目的: 提高程序效率,简化远程服务调用。

1.2 Feign

  • 诞生原因: 满足服务间调用需求。
  • 使用效果: 远程服务调用如同本地方法调用。
  • 功能特点: 参数传递与返回值解析已封装。
  • 主要作用: 提高服务间调用的开发效率。

1.3 集成Feign

1.3.1 引入依赖
  • 目标模块: 课程价格模块。
  • 依赖内容:
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-openfeignartifactId>
    dependency>
    
  • 操作: Maven重新导入依赖。
1.3.2 配置文件
  • 基本配置: 无需复杂配置,后续将介绍负载均衡配置。
1.3.3 添加注解
  • 主类注解: 在主类上添加@EnableFeignClients
  • 创建客户端: 新建CourseListClient接口。
  • 定义接口: 参考原接口定义,引入课程列表模块。
  • 配置客户端: 标明客户端并配置服务名。
  • 调用远程服务: 在控制器中实现远程调用逻辑。
  • 测试服务: 启动Eureka Server、课程列表和价格模块,验证服务间调用。

二、负载均衡策略

2.1 策略概述

  • 轮询策略: 逐个调用,循环往复。
  • 随机策略: 随机选择服务实例。

2.2 加权策略

  • 定义: 根据响应时间分配权重。
  • 作用: 评估节点性能,合理分配请求。
  • 实例: 根据CPU速度差异调整请求分配。

2.3 配置负载均衡

2.3.1 Price模块配置
  • 配置文件: 在application.properties中设置。
  • 指定策略: 配置loadbalance.rule
  • 实操配置: 在价格模块中添加配置行,选择Ribbon。
2.3.2 LoadBalancerRule
  • 概念: 定义负载均衡策略。
  • 策略类型: 如轮询、随机、加权等。
  • 选择依据: 根据业务需求和节点性能选择。
2.3.3 完成配置
  • 选择策略: 如RoundRobinRule
  • 配置完成: 实现负载均衡配置。

三、断路器Hystrix

3.1 为什么需要Hystrix

  • 服务稳定性: 无法保证每个服务100%稳定。
  • 故障影响: 一个服务故障可能导致整个系统不可用。
  • Hystrix作用: 作为保护伞,防止错误扩散,采取降级手段保证部分功能可用。

3.2 Hystrix集成

3.2.1 添加依赖
  • 位置: 课程价格模块。
  • 依赖内容:
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
    dependency>
    
3.2.2 配置文件
  • 启用Hystrix: 设置feign.hystrix.enabled=true
3.2.3 主类注解
  • 添加注解: @EnableCircuitBreaker
3.2.4 编写断路逻辑
  • Fallback类: 实现CourseListClient接口,提供默认课程列表。
  • 兜底逻辑: 返回默认课程列表,避免报错。
  • 简化返回: 业务简单时可直接返回空列表。
3.2.5 运行程序
  • 启动服务: 启动Eureka Server、课程列表和价格模块。
  • 解决冲突: 添加@Primary注解解决组件冲突。
  • 测试断路: 关闭课程列表服务,验证断路功能。

四、服务整合

4.1 编写接口

  • 目的: 整合课程列表和价格信息。
  • 方法: GET /courses-and-price
  • 返回内容: 包含课程和价格的整合对象列表。

4.2 生成实体类

  • 位置: entity包。
  • 类名: CourseAndPrice
  • 属性: 包括ID、课程ID、名称、价格。
  • 方法: 生成get和set方法。

4.3 编写服务方法

  • 目标: 在CoursePriceService中新增方法。
  • 方法: getCoursesAndPrice
  • 实现:
    • 获取课程列表。
    • 遍历列表,查询每个课程的价格。
    • 组装CourseAndPrice对象。
    • 返回整合后的列表。

4.4 启动程序

  • 步骤: 重启价格服务和课程列表服务。
  • 结果: 接口正常返回,完成服务整合。

五、总结

  • 服务间调用: 使用Feign简化远程调用。
  • 负载均衡: 配置不同策略,如轮询、随机、加权。
  • 断路器: 使用Hystrix保证服务稳定性。
  • 服务整合: 通过Feign整合课程列表和价格服务。

以上内容总结了Spring Cloud中服务间调用、负载均衡与断路器整合的核心知识点,为后续的微服务架构开发提供了坚实的基础。

Spring Cloud Zuul网关笔记

一、网关Zuul的作用

1.1 统一身份验证

  • 作用: 将身份验证逻辑集中到网关,避免每个服务重复实现。
  • 好处: 简化服务开发,提高安全性。

1.2 安全处理

  • 作用: 网关统一处理安全问题,如拦截异常IP。
  • 好处: 提高系统整体安全性,减少服务开发负担。

二、网关Zuul的集成

2.1 注册到Eureka

  • 步骤: 将Zuul注册到Eureka注册中心。
  • 目的: 使其他服务能够发现和调用Zuul。

2.2 引入依赖

  • 依赖:
    • Eureka Client: 用于与Eureka注册中心通信。
    • Zuul网关: 实现网关功能。
  • 示例:
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-zuulartifactId>
    dependency>
    

2.3 配置路由地址

  • 方法: 在配置文件中设置路由规则。
  • 示例:
    zuul.routes.course-list.path=/list/**
    zuul.routes.course-list.service-id=course-list
    

2.4 启动网关

  • 步骤: 编写启动类并运行。
  • 注解:
    • @SpringBootApplication
    • @EnableZuulProxy

2.5 访问服务

  • 方式: 通过网关访问注册在Eureka上的服务。
  • 默认路由: 网关提供默认路由地址。

2.6 自定义配置

  • 前缀配置: 为网关配置统一前缀(如/imooc)。
  • 路径和服务名配置: 为模块配置独立路径和服务名。
  • 示例:
    zuul.prefix=/imooc
    zuul.routes.course-list.path=/list/**
    zuul.routes.course-list.service-id=course-list
    

2.7 重启网关

  • 必要性: 修改配置后需重启网关以生效。
  • 验证: 检查新地址是否可用。

三、网关过滤器

3.1 过滤器类型

  • pre: 请求前运行,用于权限校验。
  • post: 请求后运行,用于统计请求时长。
  • error: 请求出错时运行。

3.2 前置过滤器编写

  • 注解: @Component
  • 继承: ZuulFilter
  • 方法实现:
    • filterType(): 返回FilterConstants.PRE_TYPE
    • filterOrder(): 指定执行顺序(如5)。
    • shouldFilter(): 返回true表示所有请求都经过此过滤器。
    • run(): 实现具体逻辑,如打印请求URL。

3.3 后置过滤器编写

  • 复制前置过滤器: 修改描述和类型。
  • 类型修改: filterType()返回FilterConstants.POST_TYPE
  • 执行顺序: 使用SEND_RESPONSE_FILTER_ORDER - 1
  • 业务逻辑: 打印返回状态码。

3.4 效果展示

  • 启动服务: 启动网关服务并访问接口。
  • 前置过滤器输出: 打印请求URL。
  • 后置过滤器输出: 打印响应状态码。

四、知识小结

知识点 核心内容 考试重点/易混淆点 难度系数
网关作用 统一身份验证和安全处理 网关如何统一处理身份验证和安全问题
网关集成 注册到Eureka,引入依赖,配置路由地址 路由地址配置的方法和个性化设置
过滤器类型 pre、post、error过滤器及其使用场景 区分pre和post的使用场景
前置过滤器编写 使用@Component注解,继承ZuulFilter,实现抽象方法 过滤器类别指定和shouldFilter方法的实现
后置过滤器编写 修改过滤器类型为post,调整执行顺序,实现具体逻辑 过滤器类别修改和返回状态码的获取
过滤器执行顺序 数字越大执行越晚,pre先执行,post后执行 理解过滤器执行顺序的重要性
实际测试 启动服务,访问接口,验证过滤器是否按预期工作 验证过滤器是否正确打印URL和状态码

五、总结

  • 网关作用: 提供统一身份验证和安全处理。
  • 网关集成: 注册到Eureka,引入依赖,配置路由地址。
  • 过滤器: 实现前置、后置和错误过滤,灵活处理请求和响应。
  • 自定义配置: 配置网关前缀和模块路径,提高路由灵活性。

以上内容总结了Spring Cloud Zuul网关的核心知识点,为后续的微服务架构开发提供了坚实的基础。

你可能感兴趣的:(java学习,java,微服务,spring)