第7章:使用Spring Boot开发微服务(Developing Microservices with Spring Boot)
一、导读
1.1 本章学习目标
第7章以 Java/Spring 生态为例,讲解如何使用 Spring Boot 和 Spring Cloud 构建微服务。从项目结构、自动配置开始,逐步介绍服务注册与发现、配置中心、API 网关、声明式客户端等核心组件。
本章的核心学习目标包括:
- 掌握 Spring Boot 的核心机制:自动配置、起步依赖、Actuator 监控。
- 学会使用 Spring Cloud 核心组件:Eureka/Consul、Config、Gateway、Feign。
- 理解服务注册与发现的实现:@EnableDiscoveryClient 的工作原理。
- 掌握配置中心的使用:Git 仓库存储配置、动态刷新。
- 学会使用 Feign 声明式客户端:简化服务间调用。
- 理解 Spring Cloud Gateway 的路由和过滤机制。
1.2 为什么本章如此重要
Spring Boot + Spring Cloud 是 Java 生态中最成熟的微服务框架。Richardson 选择 Spring 作为示例框架,不仅因为它的广泛使用,更因为它提供了微服务所需的全套基础设施——从服务注册到配置管理,从 API 网关到熔断器。
本章的重要性在于它将前面章节的理论模式落地为具体的代码实现。读者将看到一个完整的微服务从创建到部署的全过程。
1.3 核心问题引导
Spring Boot 的自动配置是如何工作的?
服务注册与发现在 Spring Cloud 中如何实现?
配置中心如何实现配置的动态刷新?
Feign 客户端如何与熔断器集成?
Spring Cloud Gateway 的路由规则如何配置?
Spring Boot Actuator 提供了哪些监控端点?
二、核心概念详解
2.1 Spring Boot 核心机制
2.1.1 自动配置
Spring Boot 的自动配置通过 @EnableAutoConfiguration 注解实现。它根据 classpath 中的依赖自动配置 Bean。
// 自动配置示例:
// 当 classpath 中有 DataSource 和 JdbcTemplate 时
// Spring Boot 自动配置:
// 1. 创建 HikariCP 连接池
// 2. 创建 JdbcTemplate Bean
// 3. 配置事务管理器
// 自动配置的条件注解:
@ConditionalOnClass(DataSource.class) // classpath 中有这个类
@ConditionalOnMissingBean(DataSource.class) // 容器中没有这个 Bean
@ConditionalOnProperty("spring.datasource.url") // 有这个配置属性
// 自定义自动配置:
@Configuration
@ConditionalOnClass(KafkaTemplate.class)
@EnableConfigurationProperties(KafkaProperties.class)
public class KafkaAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public KafkaTemplate<String, String> kafkaTemplate(
ProducerFactory<String, String> producerFactory) {
return new KafkaTemplate<>(producerFactory);
}
}2.1.2 Actuator 监控
# application.yml
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
metrics:
tags:
application: ${spring.application.name}
# 常用端点:
# GET /actuator/health → 健康检查
# GET /actuator/info → 应用信息
# GET /actuator/metrics → 指标列表
# GET /actuator/metrics/jvm.memory.used → JVM 内存使用
# GET /actuator/prometheus → Prometheus 格式指标2.2 服务注册与发现
2.2.1 Eureka 实现
# Eureka Server 配置(application.yml)
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
server:
enable-self-preservation: false
# Eureka Client 配置(order-service/application.yml)
eureka:
client:
service-url:
defaultZone: http://eureka-server:8761/eureka/
registry-fetch-interval-seconds: 5
instance:
prefer-ip-address: true
lease-renewal-interval-in-seconds: 10
lease-expiration-duration-in-seconds: 30// 服务注册
@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
// 使用 DiscoveryClient 获取服务实例
@Autowired
private DiscoveryClient discoveryClient;
public List<ServiceInstance> getOrderServiceInstances() {
return discoveryClient.getInstances("order-service");
}
// 使用 @LoadBalanced RestTemplate
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
// 使用服务名代替具体地址
restTemplate.getForObject("http://payment-service/api/payments/123", Payment.class);2.2.2 Consul 实现
# Consul Client 配置
spring:
cloud:
consul:
host: consul-server
port: 8500
discovery:
service-name: order-service
health-check-interval: 10s
health-check-path: /actuator/health
prefer-ip-address: true2.3 配置中心
2.3.1 Spring Cloud Config
# Config Server 配置
spring:
cloud:
config:
server:
git:
uri: https://github.com/ftgo/config-repo
search-paths: '{application}'
default-label: main
profiles:
active: native
# Config Client 配置(bootstrap.yml)
spring:
cloud:
config:
uri: http://config-server:8888
name: order-service
profile: ${spring.profiles.active}
fail-fast: true
retry:
initial-interval: 1000
max-interval: 5000
max-attempts: 5// Git 配置仓库结构:
// config-repo/
// ├── order-service/
// │ ├── order-service.yml (默认配置)
// │ ├── order-service-dev.yml (开发环境)
// │ └── order-service-prod.yml (生产环境)
// ├── payment-service/
// │ └── payment-service.yml
// └── application.yml (全局默认配置)
// 动态刷新配置:
// 1. 修改 Git 仓库中的配置文件
// 2. 调用 /actuator/refresh 端点(或通过 Bus 广播)
// 3. @RefreshScope 标记的 Bean 会重新加载配置
@RefreshScope
@RestController
public class OrderController {
@Value("${order.max-items:10}")
private int maxItems;
}2.4 Feign 声明式客户端
// Feign 客户端定义
@FeignClient(name = "payment-service",
fallbackFactory = PaymentClientFallback.class,
configuration = FeignClientConfig.class)
public interface PaymentClient {
@PostMapping("/api/payments")
Payment createPayment(@RequestBody PaymentRequest request);
@GetMapping("/api/payments/{id}")
Payment getPayment(@PathVariable("id") String id);
@PostMapping("/api/payments/{id}/refund")
Payment refundPayment(@PathVariable("id") String id);
}
// 降级处理
@Component
public class PaymentClientFallback implements FallbackFactory<PaymentClient> {
@Override
public PaymentClient create(Throwable cause) {
return new PaymentClient() {
@Override
public Payment createPayment(PaymentRequest request) {
throw new ServiceUnavailableException("Payment service unavailable", cause);
}
@Override
public Payment getPayment(String id) {
return Payment.defaultPayment(id);
}
@Override
public Payment refundPayment(String id) {
throw new ServiceUnavailableException("Payment service unavailable", cause);
}
};
}
}
// Feign 配置(超时、拦截器)
public class FeignClientConfig {
@Bean
public Request.Options requestOptions() {
return new Request.Options(
Duration.ofSeconds(3), // connect timeout
Duration.ofSeconds(5), // read timeout
true // follow redirects
);
}
@Bean
public RequestInterceptor authInterceptor() {
return template -> {
String token = SecurityContextHolder.getContext()
.getAuthentication().getCredentials().toString();
template.header("Authorization", "Bearer " + token);
};
}
}2.5 Spring Cloud Gateway
# Gateway 路由配置
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
- Method=GET,POST,PUT,DELETE
filters:
- StripPrefix=0
- name: CircuitBreaker
args:
name: orderCB
fallbackUri: forward:/fallback/orders
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 100
redis-rate-limiter.burstCapacity: 200
- id: payment-service
uri: lb://payment-service
predicates:
- Path=/api/payments/**
filters:
- name: CircuitBreaker
args:
name: paymentCB
fallbackUri: forward:/fallback/payments
default-filters:
- name: AddResponseHeader
args:
name: X-Gateway-Version
value: "1.0"// 自定义过滤器
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getURI().getPath();
if (path.startsWith("/api/admin")) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (token == null || !jwtUtil.validate(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}三、实战案例
3.1 案例一:完整的订单服务
@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired private OrderService orderService;
@Autowired private PaymentClient paymentClient;
@PostMapping
public ResponseEntity<Order> createOrder(@RequestBody CreateOrderRequest request) {
Order order = orderService.createOrder(request);
try {
Payment payment = paymentClient.createPayment(
new PaymentRequest(order.getId(), order.getTotalAmount()));
order.setStatus(OrderStatus.PAID);
} catch (Exception e) {
order.setStatus(OrderStatus.PAYMENT_PENDING);
}
orderService.updateOrder(order);
return ResponseEntity.status(201).body(order);
}
@GetMapping("/{id}")
public ResponseEntity<OrderDetail> getOrderDetail(@PathVariable String id) {
Order order = orderService.getOrder(id);
if (order == null) return ResponseEntity.notFound().build();
Restaurant restaurant = null;
try {
restaurant = restTemplate.getForObject(
"http://restaurant-service/api/restaurants/" + order.getRestaurantId(),
Restaurant.class);
} catch (Exception e) {
// 降级处理
}
OrderDetail detail = new OrderDetail(order, restaurant);
return ResponseEntity.ok(detail);
}
}3.2 案例二:完整的项目结构
ftgo-order-service/
├── build.gradle
├── src/main/java/com/ftgo/order/
│ ├── OrderServiceApplication.java
│ ├── config/
│ │ ├── WebConfig.java
│ │ ├── SecurityConfig.java
│ │ └── KafkaConfig.java
│ ├── controller/
│ │ └── OrderController.java
│ ├── service/
│ │ ├── OrderService.java
│ │ └── OrderServiceImpl.java
│ ├── repository/
│ │ └── OrderRepository.java
│ ├── model/
│ │ ├── Order.java
│ │ ├── OrderItem.java
│ │ └── OrderStatus.java
│ ├── dto/
│ │ ├── CreateOrderRequest.java
│ │ └── OrderDetail.java
│ ├── client/
│ │ ├── PaymentClient.java
│ │ └── PaymentClientFallback.java
│ └── event/
│ ├── OrderEventPublisher.java
│ └── OrderEventListener.java
├── src/main/resources/
│ ├── application.yml
│ └── bootstrap.yml
└── src/test/java/
└── com/ftgo/order/
├── controller/OrderControllerTest.java
└── service/OrderServiceTest.java四、本章小结
本章以 Spring Boot + Spring Cloud 为例讲解了微服务的实现:
核心知识点:
- Spring Boot 自动配置根据 classpath 依赖自动配置 Bean。
- 服务注册与发现通过 Eureka/Consul 实现,客户端使用 @LoadBalanced 负载均衡。
- 配置中心通过 Git 仓库存储配置,支持动态刷新。
- Feign 提供声明式 REST 客户端,集成熔断器实现降级。
- Spring Cloud Gateway 提供路由、过滤、限流等网关功能。
五、思考与练习
思考题
Spring Boot 自动配置的原理是什么?如何自定义自动配置?
Eureka 和 Consul 的一致性模型有何不同?
Feign 的降级策略应该如何设计?
练习题
使用 Spring Initializr 创建两个微服务项目,配置 Eureka 服务注册。
实现 Feign 客户端调用另一个服务,集成 Resilience4j 熔断器。
配置 Spring Cloud Gateway 路由规则,实现 API 路由和限流。