跳到主要内容Spring Boot 4.0 集成 Spring Cloud Alibaba 2025 实录 | 极客日志Javajava
Spring Boot 4.0 集成 Spring Cloud Alibaba 2025 实录
记录 Spring Boot 4.0 集成 Spring Cloud Alibaba 2025 的完整过程,包含 Nacos 服务发现与配置、Sentinel 限流降级、OpenFeign 远程调用,以及 Docker 部署。提供可直接使用的 pom 依赖、bootstrap/application 配置、核心代码和容器编排,适合搭建微服务基础架构。
环境与依赖
这套配置基于 JDK 21+、Maven 3.6+,Spring Boot 4.0.0,Spring Cloud 2025.0.0,Spring Cloud Alibaba 2025.0.0。如果你用的是 Gradle,把 pom 里的逻辑搬过去就行,核心依赖版本保持一致。
项目结构:
springboot4-sc-alibaba-demo/
├── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ ├── Springboot4ScAlibabaApplication.java
│ │ ├── controller/
│ │ ├── service/
│ │ └── config/
│ └── resources/
│ ├── application.yml
│ └── bootstrap.yml
├── pom.xml
└── README.md
依赖管理
pom.xml 先声明 parent 为 spring-boot-starter-parent:4.0.0,再把 Spring Cloud 和 Alibaba 的 BOM 用 dependencyManagement 引进来管理版本。实际用到的 starter 再写到 dependencies 里。
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.0
com.example
springboot4-sc-alibaba-demo
1.0.0
springboot4-sc-alibaba-demo
Spring Boot 4.0 with Spring Cloud Alibaba 2025
21
21
21
UTF-8
2025.0.0
2025.0.0
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
${spring-cloud-alibaba.version}
pom
import
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-actuator
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.cloud
spring-cloud-starter-loadbalancer
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
</version>
<relativePath/>
</parent>
<groupId>
</groupId>
<artifactId>
</artifactId>
<version>
</version>
<name>
</name>
<description>
</description>
<properties>
<java.version>
</java.version>
<maven.compiler.source>
</maven.compiler.source>
<maven.compiler.target>
</maven.compiler.target>
<project.build.sourceEncoding>
</project.build.sourceEncoding>
<spring-cloud.version>
</spring-cloud.version>
<spring-cloud-alibaba.version>
</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
<version>
</version>
<type>
</type>
<scope>
</scope>
</dependency>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
<version>
</version>
<type>
</type>
<scope>
</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
</dependency>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
</dependency>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
</dependency>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
</dependency>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
</dependency>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
</dependency>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
</dependency>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
<scope>
</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>
</groupId>
<artifactId>
</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件
bootstrap.yml 负责引导阶段配置,主要指定 Nacos 服务地址、命名空间,以及 Sentinel 仪表盘连接。application.yml 放应用端口、上下文路径、Feign 超时之类的常规设置。
spring:
application:
name: demo-service
profiles:
active: dev
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yaml
namespace: dev-namespace
group: DEFAULT_GROUP
extension-configs:
- data-id: shared-config.yaml
group: SHARED_GROUP
refresh: true
discovery:
server-addr: localhost:8848
namespace: dev-namespace
group: DEFAULT_GROUP
metadata:
version: 1.0
sentinel:
transport:
dashboard: localhost:8080
eager: true
datasource:
ds1:
nacos:
server-addr: localhost:8848
data-id: ${spring.application.name}-sentinel
group-id: DEFAULT_GROUP
data-type: json
rule-type: flow
server:
port: 8081
servlet:
context-path: /demo
spring:
main:
allow-bean-definition-overriding: true
mvc:
throw-exception-if-no-handler-found: true
web:
resources:
add-mappings: false
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
feign:
sentinel:
enabled: true
client:
config:
default:
connect-timeout: 5000
read-timeout: 5000
logger-level: basic
logging:
level:
com.example: debug
com.alibaba.nacos: warn
应用入口
主类加上 @EnableDiscoveryClient 和 @EnableFeignClients,打开服务注册和远程调用。实际打印一点启动信息,方便确认环境。
package com.example;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.core.env.Environment;
import java.util.Objects;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.example.service")
public class Springboot4ScAlibabaApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(Springboot4ScAlibabaApplication.class)
.bannerMode(Banner.Mode.CONSOLE)
.run(args);
}
public void printInfo(Environment env) {
String port = env.getProperty("server.port");
String contextPath = Objects.requireNonNullElse(
env.getProperty("server.servlet.context-path"), "");
System.out.println("\n----------------------------------------------------------");
System.out.println("Application is running! Access URLs:");
System.out.println("Local: \t\thttp://localhost:" + port + contextPath);
System.out.println("External: \thttp://" + getHostAddress() + ":" + port + contextPath);
System.out.println("Profile(s): \t" + String.join(",", env.getActiveProfiles()));
System.out.println("----------------------------------------------------------\n");
}
private String getHostAddress() {
try {
return java.net.InetAddress.getLocalHost().getHostAddress();
} catch (Exception e) {
return "127.0.0.1";
}
}
}
服务提供与消费
提供者
一个简单的 Controller 返回端口信息,用来验证负载均衡。
package com.example.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class DemoController {
@Value("${server.port}")
private String port;
@GetMapping("/hello")
public String hello(@RequestParam String name) {
return String.format("Hello %s, from port: %s", name, port);
}
@GetMapping("/health")
public String health() {
return "Service is healthy";
}
}
消费者(OpenFeign)
用 @FeignClient 定义远程接口,并配置 Fallback 做容错。
package com.example.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = "demo-service", path = "/demo/api", fallback = DemoServiceFallback.class)
public interface DemoServiceClient {
@GetMapping("/hello")
String sayHello(@RequestParam String name);
@GetMapping("/health")
String healthCheck();
}
package com.example.service;
import org.springframework.stereotype.Component;
@Component
public class DemoServiceFallback implements DemoServiceClient {
@Override
public String sayHello(String name) {
return "Fallback: Hello " + name;
}
@Override
public String healthCheck() {
return "Fallback: Service unavailable";
}
}
package com.example.controller;
import com.example.service.DemoServiceClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private DemoServiceClient demoServiceClient;
@GetMapping("/call-hello")
public String callHello(@RequestParam String name) {
return demoServiceClient.sayHello(name);
}
}
配置中心
Nacos 里的配置可以用 @RefreshScope 动态刷新,应用里加一个 Controller 就能看到效果。
先在 Nacos 控制台创建 demo-service-dev.yaml:
app:
config:
demo: "This is dynamic config from Nacos"
feature:
enabled: true
server:
port: 8081
logging:
level:
com.example: info
package com.example.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {
@Value("${app.config.demo:defaultValue}")
private String demoConfig;
@Value("${app.feature.enabled:false}")
private boolean featureEnabled;
@GetMapping("/demo")
public String getDemoConfig() {
return "Current config: " + demoConfig + ", Feature enabled: " + featureEnabled;
}
}
Sentinel 流量控制
全局统一处理限流响应,同时用 @SentinelResource 标注需要保护的方法。
自定义 BlockExceptionHandler:
package com.example.config;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class SentinelConfig {
@Bean
public BlockExceptionHandler sentinelBlockExceptionHandler() {
return new CustomBlockExceptionHandler();
}
static class CustomBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
BlockException e) throws Exception {
Map<String, Object> result = new HashMap<>();
if (e instanceof FlowException) {
result.put("code", 1001);
result.put("message", "接口限流");
} else if (e instanceof DegradeException) {
result.put("code", 1002);
result.put("message", "服务降级");
} else if (e instanceof ParamFlowException) {
result.put("code", 1003);
result.put("message", "热点参数限流");
} else if (e instanceof SystemBlockException) {
result.put("code", 1004);
result.put("message", "系统规则限制");
} else if (e instanceof AuthorityException) {
result.put("code", 1005);
result.put("message", "授权规则不通过");
} else {
result.put("code", 1000);
result.put("message", "未知限流");
}
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.setCharacterEncoding("UTF-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getWriter(), result);
}
}
}
package com.example.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/sentinel")
public class SentinelController {
@GetMapping("/resource")
@SentinelResource(value = "protectedResource",
blockHandler = "handleBlock",
fallback = "handleFallback")
public String protectedResource() {
return "This is a protected resource";
}
public String handleBlock(BlockException ex) {
return "请求过于频繁,请稍后重试";
}
public String handleFallback(Throwable ex) {
return "服务暂时不可用,请稍后重试";
}
}
其他实用配置
自定义负载均衡
Spring Cloud LoadBalancer 可以按需定制,这里给出一个简单的路由逻辑占位。
package com.example.config;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Flux;
import java.util.Arrays;
import java.util.List;
@Configuration
public class LoadBalancerConfig {
@Bean
public ServiceInstanceListSupplier serviceInstanceListSupplier() {
return new DemoServiceInstanceListSupplier("demo-service");
}
static class DemoServiceInstanceListSupplier implements ServiceInstanceListSupplier {
private final String serviceId;
public DemoServiceInstanceListSupplier(String serviceId) {
this.serviceId = serviceId;
}
@Override
public String getServiceId() {
return serviceId;
}
@Override
public Flux<List<ServiceInstance>> get() {
return Flux.just(Arrays.asList());
}
}
}
全局异常处理
package com.example.config;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Map<String, Object> handleException(Exception e) {
Map<String, Object> result = new HashMap<>();
result.put("code", 500);
result.put("message", "系统异常:" + e.getMessage());
return result;
}
}
本地开发与容器化
本机调试先启动 Nacos 和 Sentinel Dashboard:
sh nacos/bin/startup.sh -m standalone
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -jar sentinel-dashboard.jar
然后启动应用,访问 /actuator/services 可以查看已注册的服务。
package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/actuator")
public class ActuatorController {
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/services")
public List<String> getServices() {
return discoveryClient.getServices().stream()
.map(service -> service + ": " + discoveryClient.getInstances(service))
.collect(Collectors.toList());
}
@GetMapping("/info")
public String info() {
return "Spring Boot 4.0 with Spring Cloud Alibaba 2025";
}
}
FROM openjdk:21-jdk-slim
WORKDIR /app
COPY target/springboot4-sc-alibaba-demo-1.0.0.jar app.jar
EXPOSE 8080
ENV JAVA_OPTS="-Xmx512m -Xms256m -Djava.security.egd=file:/dev/./urandom"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
用 Compose 一键编排 Nacos、Sentinel 和应用:
version: '3.8'
services:
nacos:
image: nacos/nacos-server:latest
container_name: nacos-server
environment:
- MODE=standalone
ports:
- "8848:8848"
networks:
- sc-alibaba-net
sentinel:
image: bladex/sentinel-dashboard:latest
container_name: sentinel-dashboard
ports:
- "8080:8080"
networks:
- sc-alibaba-net
demo-app:
build: .
container_name: demo-service
ports:
- "8081:8081"
environment:
- SPRING_PROFILES_ACTIVE=docker
- SPRING_CLOUD_NACOS_CONFIG_SERVER-ADDR=nacos:8848
- SPRING_CLOUD_NACOS_DISCOVERY_SERVER-ADDR=nacos:8848
- SPRING_CLOUD_SENTINEL_TRANSPORT_DASHBOARD=sentinel:8080
depends_on:
- nacos
- sentinel
networks:
- sc-alibaba-net
networks:
sc-alibaba-net:
driver: bridge
把这一整套搭起来,一个包含服务发现、配置中心、流量控制和远程调用的微服务骨架就有了。生产环境要再仔细调整安全策略、监控探针和资源限制,但作为起点,这些配置可以直接用。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online