Spring Boot 4.1 官方 gRPC 支持源码剖析:从社区 Starter 到一等公民

本文是 Spring Boot 4 系列第 15 篇 | 基于 Spring Boot 4.1.0 仓库(module/spring-boot-grpc-*)与官方文档 io/grpc.adoc | 预计阅读 25 分钟

文末附「完整链路图」与「四个核心设计思想」,从社区 Starter 迁移时可直接对照。


写在前面

微服务内部调用,JSON over HTTP 有两个老问题:一是 JSON 文本体积大,{"name":"zhangsan","age":18} 里键名和引号占了近一半流量;二是跨语言契约难维护,字段命名、类型映射全靠两边对着文档对齐。

gRPC 是业界公认的解法:.proto 文件定义契约,Protocol Buffers 二进制编码体积小一个量级,HTTP/2 多路复用省掉连接开销。但 Java 服务用 gRPC 一直有个别扭的地方------没有官方支持 。要么用 yidongnan 的社区 grpc-spring-boot-starter(维护靠个人、版本跟着社区走),要么自己手写 ServerBuilder 装配。

Spring Boot 4.1 补上了这块。官方文档(io/grpc.adoc)开篇第一句:

"Spring Boot include support for developing and testing both client and server gRPC applications."

而且它没有再造一个轮子,而是建立在 Spring 官方新项目 Spring gRPC (org.springframework.grpc:spring-grpc-core,1.1.0)之上:Boot 负责自动配置,Spring gRPC 负责核心抽象------与 Boot 和 Spring Data、Spring Security 的关系一致。本文从依赖到测试把这套架构逐层拆开。

内容速览

  • 全景图:三层结构(应用/自动配置/Spring gRPC 核心)+ 三条 Transport
  • 三个模块 + 四个 Starter,Server Starter 默认带 gRPC Reflection 服务
  • 服务端契约是 BindableService Bean,@GrpcService 只是便捷注解
  • 三个条件触发服务端自动配置:没有服务就不起服务器
  • 三种服务器形态:Netty(默认)/ Servlet / InProcess 的条件分派
  • 纠偏:Boot 4.1 没有 @GrpcClient,官方模型是 @ImportGrpcClients 导入 stub
  • 命名通道:一个通道对应一个远端服务,多个 stub 可共享
  • @AutoConfigureTestGrpcTransport 零配置进程内测试,与真端口随机测试两条路
  • 官方 vs 社区 Starter 对照表与迁移建议
  • 六个边界与坑:配置前缀别混、Transport 互斥、TLS 走 SslBundle

一、全景图:三层结构 + 三条 Transport

java 复制代码
┌─────────────────────────────────────────────────────────┐
│ 应用层(你写的代码)                                       │
│  Server: @GrpcService 实现类(继承 .proto 生成的 ImplBase) │
│  Client: @ImportGrpcClients 导入 stub + 直接注入 stub Bean │
│  Test:   @AutoConfigureTestGrpcTransport / @LocalGrpcServerPort
└─────────────────────────────────────────────────────────┘
                          │
┌─────────────────────────────────────────────────────────┐
│ Boot 4.1 自动配置层(module/spring-boot-grpc-*)          │
│  GrpcServerAutoConfiguration(spring.grpc.server.*)      │
│  GrpcClientAutoConfiguration(spring.grpc.client.*)      │
│  TestGrpcTransportAutoConfiguration(spring-boot-grpc-test)│
└─────────────────────────────────────────────────────────┘
                          │
┌─────────────────────────────────────────────────────────┐
│ Spring gRPC 1.1.0 核心层(org.springframework.grpc)       │
│  GrpcServerFactory / GrpcChannelFactory / GrpcServiceDiscoverer
│  GrpcServerLifecycle / GrpcService / ImportGrpcClients     │
└─────────────────────────────────────────────────────────┘
                          │
┌─────────────────────────────────────────────────────────┐
│ Transport(gRPC Java 1.80.0)                            │
│  Netty(默认,端口 9090)/ Netty Shaded / Servlet(web 容器)
│  InProcess(进程内,测试与同进程通信)                      │
└─────────────────────────────────────────────────────────┘

依赖坐标(platform/spring-boot-dependencies/build.gradle 实证):

版本 说明
gRPC Java(grpc-bom) 1.80.0 Netty/Stub/InProcess/Servlet 等全部 io.grpc 构件
Spring gRPC(spring-grpc-core) 1.1.0 核心抽象:工厂、服务发现、生命周期、注解

二、第一层:模块与 Starter------官方支持长什么样

2.1 三个模块 + 四个 Starter

Spring Boot 4.1.0 仓库里有三个 gRPC 模块:

arduino 复制代码
module/spring-boot-grpc-server   # Server 自动配置
module/spring-boot-grpc-client   # Client 自动配置
module/spring-boot-grpc-test     # 测试支持(In-Process Transport)

四个 Starter POM(starter/ 目录):

Starter 依赖(build.gradle 实证)
spring-boot-starter-grpc-server spring-boot-starter + spring-boot-grpc-server + grpc-netty + grpc-services
spring-boot-starter-grpc-client spring-boot-starter + spring-boot-grpc-client + grpc-netty + grpc-stub
spring-boot-starter-grpc-server-test 面向 server 测试
spring-boot-starter-grpc-client-test 面向 client 测试

两个细节:

  1. Server Starter 默认带上 grpc-services ------官方文档说的"gRPC Reflection 服务"(让 grpcurl 等客户端在线浏览服务元数据、下载 .proto)默认可用,不需要额外加依赖;
  2. Netty 是默认 Transport :Starter 直接引入 grpc-netty,"零配置起服务"背后是 Netty 服务器在监听。

仓库里还有 8 个 smoke-test(smoke-test/spring-boot-smoke-test-grpc-*:server、client、secure、oauth、servlet、netty-shaded、test......),官方用它们验证每种形态都能跑通,可以直接当作官方用法示例集。


三、第二层:Server 自动配置------从 @GrpcService 到监听端口

3.1 服务端开发模型:@GrpcService + ImplBase

先看服务端怎么写。.proto 文件经 protobuf 插件生成 HelloWorldGrpc 基类,实现类继承它并标上 Spring gRPC 的 @GrpcService 注解(org.springframework.grpc.server.service.GrpcService),交给组件扫描即可。仓库 smoke-test 的 HelloWorldService(smoke-test/spring-boot-smoke-test-grpc-server)就是标准写法:

java 复制代码
@GrpcService
public class HelloWorldService extends HelloWorldGrpc.HelloWorldImplBase {

	private static Log logger = LogFactory.getLog(HelloWorldService.class);

	@Override
	public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
		String name = request.getName();
		logger.info("sayHello " + name);
		Assert.isTrue(!name.startsWith("error"), () -> "Bad name: " + name);
		Assert.state(!name.startsWith("internal"), "Internal error");
		String message = "Hello '%s'".formatted(name);
		HelloReply reply = HelloReply.newBuilder().setMessage(message).build();
		responseObserver.onNext(reply);
		responseObserver.onCompleted();
	}
}

官方文档的原话定义了契约:

"Spring gRPC will automatically expose any bean that implements io.grpc.BindableService as a gRPC server."

也就是说:只要是 BindableService 类型的 Bean,就会被自动发布成 gRPC 服务 ------生成的 stub 基类天然实现 BindableService,@GrpcService 只是让普通实现类变成 Bean 的便捷注解。反编译 spring-grpc-core 1.1.0 的字节码可以看到它的元注解(@TargetTYPE + METHOD):

java 复制代码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Service                      // 类上:@Service 语义,组件扫描即可发现
@Bean                         // 方法上:配合 @Bean 工厂方法使用
public @interface GrpcService {
	Class<? extends ServerInterceptor>[] interceptors() default {};
}

两个细节:类级用法靠 @Service 让组件扫描发现;方法级用法(@Target 里的 METHOD)靠 @Bean 支持"在一个配置类里用 @Bean 方法声明服务";interceptors() 属性还可以给单个服务指定专属拦截器。

3.2 GrpcServerAutoConfiguration:三个条件触发

module/spring-boot-grpc-server/.../autoconfigure/GrpcServerAutoConfiguration.java(@since 4.1.0):

java 复制代码
@AutoConfiguration
@ConditionalOnClass({ GrpcServerFactory.class, Grpc.class })
@ConditionalOnBean(BindableService.class)
@ConditionalOnBooleanProperty(name = "spring.grpc.server.enabled", matchIfMissing = true)
@EnableConfigurationProperties(GrpcServerProperties.class)
@Import({ GrpcServerCodecConfiguration.class, ServletGrpcServerConfiguration.class,
		ShadedNettyGrpcServerConfiguration.class, NettyGrpcServerConfiguration.class,
		InProcessGrpcServerConfiguration.class })
public final class GrpcServerAutoConfiguration {
	// ...
}

三个条件:

  • @ConditionalOnClass({ GrpcServerFactory.class, Grpc.class }):classpath 上有 Spring gRPC 核心类 + gRPC 本体(Starter 已经满足);
  • @ConditionalOnBean(BindableService.class) :容器里至少有一个 gRPC 服务实现------没有服务就不起服务器,这是"无服务零开销"的关键;
  • @ConditionalOnBooleanProperty("spring.grpc.server.enabled", matchIfMissing = true) :默认开启,spring.grpc.server.enabled=false 可关。

@Import 一次性把四种服务器形态的配置类全部拉进来,由各自的条件决定谁生效(见 3.4)。

3.3 GrpcServerProperties:spring.grpc.server.*

属性前缀是 spring.grpc.server (GrpcServerProperties,@ConfigurationProperties("spring.grpc.server"))。注意一个流传说法要纠偏:社区 Starter 用的是 grpc.server.port,官方 Boot 4.1 的配置是 spring.grpc.server.port。主要子项(源码字段实证):

属性 默认值 说明
spring.grpc.server.port 9090(Netty 默认) 监听端口,0 表示绑定动态端口(测试常用)
spring.grpc.server.address --- 绑定地址
spring.grpc.server.shutdown.grace-period 30s 优雅停机宽限期
spring.grpc.server.inbound.message.max-size 4MB 入站消息上限
spring.grpc.server.inbound.metadata.max-size 8KB 元数据上限
spring.grpc.server.inprocess.name --- 设置后启用 In-Process 服务器
spring.grpc.server.keepalive.time 2h keepalive 间隔
spring.grpc.server.keepalive.timeout 20s keepalive 超时
spring.grpc.server.ssl.* 关闭 TLS(bundle 引用 spring.ssl.bundle.jks)

3.4 三种服务器:Netty / Servlet / InProcess 的条件分派

@Import 拉进来的三个工厂配置类,靠条件互斥:

① NettyGrpcServerConfiguration(默认):

java 复制代码
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(NettyServerBuilder.class)
@ConditionalOnMissingNetworkGrpcServer
@ConditionalOnGrpcServerFactoryEnabled
class NettyGrpcServerConfiguration {

	@Bean
	NettyGrpcServerFactory nettyGrpcServerFactory(GrpcServerProperties properties,
			GrpcServiceDiscoverer serviceDiscoverer, GrpcServiceConfigurer serviceConfigurer,
			GrpcServerBuilderCustomizers grpcServerBuilderCustomizers, SslBundles bundles,
			ObjectProvider<GrpcServerFactoryCustomizer> customizers) {
		NettyAddress address = NettyAddress.fromProperties(properties);
		ServerCredentials credentials = ServerCredentials.get(properties.getSsl(), bundles, ...);
		NettyGrpcServerFactory factory = new NettyGrpcServerFactory(address.toString(), ...);
		customizers.orderedStream().forEach((customizer) -> customizer.customize(factory));
		serviceDiscoverer.findServices()
			.stream()
			.map((spec) -> serviceConfigurer.configure(spec, factory))
			.forEach(factory::addService);
		return factory;
	}

	@Bean
	@ConditionalOnMissingBean(name = "nettyGrpcServerLifecycle")
	GrpcServerLifecycle nettyGrpcServerLifecycle(NettyGrpcServerFactory factory, GrpcServerProperties properties,
			ApplicationEventPublisher eventPublisher) {
		return new GrpcServerLifecycle(factory, properties.getShutdown().getGracePeriod(), eventPublisher);
	}
}

三个关键抽象都来自 Spring gRPC 核心:

  • GrpcServiceDiscoverer :扫描容器里所有 BindableService Bean,得到服务清单(默认实现 DefaultGrpcServiceDiscoverer);
  • GrpcServiceConfigurer :把每个服务配置到指定工厂(默认实现 DefaultGrpcServiceConfigurer,负责拦截器等织入);
  • GrpcServerLifecycle :把工厂包装成 Spring Lifecycle,跟随容器 start/stop,停机时应用 shutdown.grace-period 优雅退出。

② ServletGrpcServerConfiguration :把 gRPC 跑在现有 Web 容器里(Tomcat/Jetty 等),条件是 @ConditionalOnWebApplication(type = SERVLET) + classpath 有 GrpcServlet(grpc-servlet-jakarta)+ spring.grpc.server.servlet.enabled(默认 true)。此时通过 GrpcServletRegistration(一个 DynamicRegistrationBean)把 gRPC 的 Servlet 注册进 Web 容器,与你的 HTTP 接口共用端口 ------注意 Netty 与 Servlet 互斥(@ConditionalOnMissingNetworkGrpcServer)。

③ InProcessGrpcServerConfiguration :设置 spring.grpc.server.inprocess.name 后启用,创建 InProcessGrpcServerFactory------服务器在进程内、不监听任何网络端口,客户端用 in-process:<name> 作为 target 就能连上(测试和同进程通信用)。

3.5 附加能力:异常处理、健康、安全、可观测

  • @GrpcAdvice 异常处理 :GrpcServerAutoConfiguration 里有一个 GrpcAdviceConfiguration(@ConditionalOnBean(annotation = GrpcAdvice.class)),与 Spring MVC 的 @ControllerAdvice 同构------用 @GrpcAdvice 标注的类里的 @GrpcExceptionHandler 方法会把业务异常映射成 gRPC Status;默认还有一个 GrpcExceptionHandlerInterceptor(@GlobalServerInterceptor)兜底;
  • 健康检查 :spring-boot-grpc-serverhealth 包(GrpcServerHealthStatusMapper 等)与 spring-boot-health 模块联动,gRPC 健康检查协议(grpc-health-probe)可用;
  • 安全 :spring-boot-security / spring-boot-security-oauth2-resource-server 是 optional 依赖,仓库里有 spring-boot-smoke-test-grpc-server-secure-oauth 两个 smoke-test 验证;
  • 可观测性 :GrpcServerObservationAutoConfiguration(optional),gRPC 调用自动接入 Micrometer Observation,与系列第十三篇的可观测性体系打通。

四、第三层:Client 自动配置------没有 @GrpcClient,只有 @ImportGrpcClients

4.1 先说结论:Boot 4.1 没有 @GrpcClient

很多迁移文章会写"官方支持 @GrpcClient 注解"------这是错的@GrpcClient 是社区 Starter(yidongnan/grpc-spring-boot-starter)的 API,Spring Boot 4.1.0 全仓库搜不到这个注解。官方客户端模型是:@ImportGrpcClients 导入 stub 类 → stub 变成普通 Bean 直接注入

看仓库 smoke-test 的 SampleGrpcClientApplication(smoke-test/spring-boot-smoke-test-grpc-client):

java 复制代码
@SpringBootApplication
@ImportGrpcClients(types = HelloWorldBlockingStub.class)
public class SampleGrpcClientApplication {

	@Bean
	ApplicationRunner applicationRunner(HelloWorldBlockingStub hello) {
		return (args) -> {
			HelloRequest request = HelloRequest.newBuilder().setName("Spring").build();
			HelloReply reply = hello.sayHello(request);
			System.out.println(">>> " + reply.getMessage());
		};
	}
}

@ImportGrpcClients(org.springframework.grpc.client.ImportGrpcClients)把生成的 HelloWorldBlockingStub 注册成 Bean,构造函数里注入即用。官方文档的说明:

"Each import includes a target which can either be a logical channel name, or the base URL of the remote server. We typically recommend using channel names rather than hard-coding targets. ... If you don't specify a target then 'default' is used."

即:不指定 target 就用名为 default 的通道。这与社区 Starter 的"每个 stub 一个地址"模型很不一样------官方把通道作为一等概念,一个通道对应一个远端服务,多个 stub 可以共享。

4.2 GrpcClientAutoConfiguration:命名通道 + 条件

module/spring-boot-grpc-client/.../autoconfigure/GrpcClientAutoConfiguration.java(@since 4.1.0):

java 复制代码
@AutoConfiguration(before = CompositeChannelFactoryAutoConfiguration.class)
@ConditionalOnClass({ AbstractStub.class, GrpcChannelBuilderCustomizer.class })
@ConditionalOnProperty(name = "spring.grpc.client.enabled", matchIfMissing = true)
@EnableConfigurationProperties(GrpcClientProperties.class)
@Import({ GrpcClientCodecConfiguration.class, ShadedNettyGrpcClientConfiguration.class,
		NettyGrpcClientConfiguration.class, InProcessGrpcClientConfiguration.class })
public final class GrpcClientAutoConfiguration {
	// ...
}

与 Server 侧对称:默认开启(spring.grpc.client.enabled)、@Import 拉入四种 Transport 配置(Netty/ShadedNetty/InProcess 互斥或叠加)。它注册的 Bean 里有两个关键角色:

  • GrpcChannelBuilderCustomizers:把属性集中应用到每个 Channel 的 builder 上(keepalive、消息上限、压缩器等);
  • PropertiesChannelCredentialsProvider :从 spring.grpc.client.channel.<name>.ssl.*SslBundles 提供 TLS 凭证。

4.3 GrpcClientProperties:命名通道怎么配

属性前缀 spring.grpc.client ,核心结构是命名通道 Map(源码字段实证):

java 复制代码
@ConfigurationProperties("spring.grpc.client")
public class GrpcClientProperties {
	private final Map<String, Channel> channel = new LinkedHashMap<>();
	// Channel: target / userAgent / bypassCertificateValidation / serviceConfig
	//          inbound(message.maxSize=4MB, metadata.maxSize=8KB)
	//          default(deadline, loadBalancingPolicy="round_robin")
	//          idle.timeout=20s / keepalive(time=5m, timeout=20s) / ssl / health
}

每个通道的 target 默认是 static://localhost:9090(与 Server 默认端口 9090 对应)。配置一个命名通道:

yaml 复制代码
spring:
  grpc:
    client:
      channel:
        myservice:
          target: static://grpc.example.com:9090
          keepalive:
            timeout: 40s
          inbound:
            message:
              max-size: 8MB

(属性路径以 GrpcClientProperties 的类结构为准:keepalive 是 Channel 层的分组,不是 inbound 的子项。)

于是客户端从"注入 stub"到"连上服务"的完整链路是:

python 复制代码
@ImportGrpcClients(types = HelloWorldBlockingStub.class)
  → 注册 stub Bean(绑定通道名 "default" 或指定的名字)
  → GrpcChannelFactory 按通道名创建 Channel(Netty,应用属性定制)
  → stub 注入业务 Bean,sayHello() 调用通过 Channel 发出

4.4 客户端三种 Transport 与 TLS

  • Netty(默认,Starter 已带);
  • Shaded Netty :与 Server 侧对应,Netty 与别的库冲突时切换 grpc-netty-shaded;
  • In-Process :classpath 有 grpc-inprocess 时可用,target 写 in-process:<name>,配合 3.4 的 In-Process 服务器做同进程通信;
  • TLS :spring.grpc.client.channel.<name>.ssl.* + spring.ssl.bundle.jks 的 SslBundle 体系,支持单向与双向(mTLS)。

五、第四层:测试支持------In-Process Transport 与随机端口

5.1 @AutoConfigureTestGrpcTransport:零配置的进程内测试

module/spring-boot-grpc-test 是专门为测试准备的模块(spring-boot-starter-grpc-client-test / -server-test 对应两个 Starter)。核心是 @AutoConfigureTestGrpcTransport 注解(org.springframework.boot.grpc.test.autoconfigure)和它背后的 TestGrpcTransportAutoConfiguration。官方文档原文:

"The @AutoConfigureTestGrpcTransport annotation allows you to quickly replace gRPC communication channels with in-process channels specifically designed for testing. Unlike regular in-process channels, these test channels do not require any configuration."

它做的事情(TestGrpcTransportAutoConfiguration 源码实证):

java 复制代码
@AutoConfiguration(before = { GrpcServerAutoConfiguration.class, GrpcClientAutoConfiguration.class })
@ConditionalOnClass({ InProcessServerBuilder.class, InProcessGrpcServerFactory.class })
public final class TestGrpcTransportAutoConfiguration {

	private static final String address = InProcessServerBuilder.generateName();
	// ...
	@Bean
	@Order(Ordered.HIGHEST_PRECEDENCE)
	TestGrpcServerFactory testGrpcServerFactory(GrpcServiceDiscoverer serviceDiscoverer, ...) { ... }

	@Bean
	@Order(Ordered.HIGHEST_PRECEDENCE)
	TestGrpcChannelFactory testGrpcChannelFactory(ClientInterceptorsConfigurer interceptorsConfigurer) { ... }
}

三个要点:

  1. InProcessServerBuilder.generateName() 生成共享地址 ------Server 工厂和 Channel 工厂共用同一个进程内地址,两端自动对上,不需要配置任何 target;
  2. @Order(HIGHEST_PRECEDENCE) 的工厂 Bean 顶替自动配置 (它声明在 GrpcServerAutoConfiguration/GrpcClientAutoConfiguration 之前执行),所以网络工厂不会启动、不占端口;
  3. 测试在进程内跑,速度快,且不会误连真实服务。

5.2 @LocalGrpcServerPort:要真端口也可以

如果测试想用真实网络连接(比如验证与真实客户端的兼容性),官方推荐随机端口:spring.grpc.server.port=0,再用 @LocalGrpcServerPort 注解注入实际端口。仓库 smoke-test 的做法(SampleGrpcServerApplicationTests):

java 复制代码
@SpringBootTest(properties = "spring.grpc.server.port=0")
class SampleGrpcServerApplicationTests {
	// ...
}

官方文档原文:

"To start a gRPC server using a random port, set spring.grpc.server.port to 0. You can use the @LocalGrpcServerPort annotation to obtain the actual port that the server started on."


六、官方方案 vs 社区 Starter:一张表看清差异

维度 Boot 4.1 官方(spring-grpc) 社区 grpc-spring-boot-starter
服务端注解 @GrpcService(org.springframework.grpc.server.service) @GrpcService(net.devh.boot.grpc.server)
客户端注解 @ImportGrpcClients(types = ...) 导入 stub @GrpcClient("name") 注入 stub
通道模型 命名通道(spring.grpc.client.channel.<name>.*),默认通道 default 每个 @GrpcClient 独立配置
Server 配置 spring.grpc.server.port grpc.server.port
测试支持 官方 spring-boot-grpc-test + @AutoConfigureTestGrpcTransport 依赖 GrpcCleanupRule 等手动组装
维护方 Spring 官方(Spring gRPC 项目,Boot 管理版本) 社区维护
底层 Spring gRPC 1.1.0 + gRPC Java 1.80.0(Boot BOM 统一管理) 随社区版本

迁移建议 :从社区 Starter 迁到官方,服务端代码几乎不动(都是继承生成的 ImplBase),主要改三处:@GrpcService 的 import、@GrpcClient@ImportGrpcClients + stub Bean 注入、配置前缀 grpc.*spring.grpc.*


七、边界与坑

  1. 服务发现只认 Bean,不认包扫描 :官方文档说得直白------"any bean that implements BindableService"。实现类必须真的是 Bean(@GrpcService@Component 都行),只是"有 .proto 生成类"不会自动发布;
  2. 没有服务就不起服务器 :@ConditionalOnBean(BindableService.class) 意味着一个只有客户端、没有服务实现的 jar 即使带上 server starter 也不会监听端口------这是特性不是 bug,但排查"服务器没起来"时要先想到它;
  3. grpc.server.*spring.grpc.server.* 别混:社区与官方的配置前缀不同,迁移时 IDE 的配置提示(来自 Boot 的配置元数据)是最可靠的对照;
  4. In-Process 服务器不是测试专属 :spring.grpc.server.inprocess.name 设了就会启动进程内服务器,in-process:<name> 可以跨应用上下文使用;@AutoConfigureTestGrpcTransport 才是"为测试特制、零配置"的通道;
  5. Servlet 模式与 Netty 互斥 :@ConditionalOnMissingNetworkGrpcServer 保证同一时刻只有一个网络服务器工厂,想切换模式就切换 classpath(grpc-netty vs grpc-servlet-jakarta),不要两个都配;
  6. TLS 走 spring.ssl.bundle.* :官方方案复用 Boot 4 的 SslBundle 体系(spring.ssl.bundle.jks 定义证书包,spring.grpc.server.ssl.bundle/spring.grpc.client.channel.<name>.ssl.bundle 引用),与 HTTP 客户端共用同一套证书管理。

八、总结

用一张图回顾:

sql 复制代码
.proto 文件
  → protobuf 插件生成 HelloWorldGrpc / stub / 消息类
  → Server:@GrpcService 实现类(BindableService Bean)
       → GrpcServerAutoConfiguration(spring.grpc.server.*)
       → GrpcServiceDiscoverer 收集服务 → NettyGrpcServerFactory(端口 9090)
       → GrpcServerLifecycle 随容器启停(grace-period 优雅停机)
  → Client:@ImportGrpcClients 导入 stub
       → GrpcClientAutoConfiguration(spring.grpc.client.channel.<name>.*)
       → GrpcChannelFactory 建 Channel → stub 直接注入使用
  → Test:@AutoConfigureTestGrpcTransport(进程内,零配置)
          或 spring.grpc.server.port=0 + @LocalGrpcServerPort

四个核心设计思想:

  1. 框架分层复用:Boot 不实现 gRPC,而是自动配置 Spring gRPC(官方新项目)------与 Spring Data/Security 的模式一致,职责清晰;
  2. 条件装配的"按需启动" :@ConditionalOnBean(BindableService.class) 让"没有服务不监听端口",@ConditionalOnMissingNetworkGrpcServer 让 Transport 互斥------自动配置的条件是行为契约,不只是依赖声明;
  3. 通道(Channel)是一等概念:客户端模型从社区的"注解标记"进化到"命名通道 + stub Bean",一个通道对应一个服务,配置集中、可共享、可替换(测试时整个通道层被替换成 In-Process);
  4. 测试基础设施与生产同构 :@AutoConfigureTestGrpcTransport@Order(HIGHEST_PRECEDENCE) 的工厂 Bean 顶替生产工厂------底层就是优先级最高的 Bean 替换,这也是 Boot 一贯的做法。

相关推荐
拖孩1 小时前
一个人 + AI 做的小程序,一个月赚了 36 块
前端·后端·微信小程序
huaweichenai1 小时前
spring boot 打包并部署到线上服务
java·数据库·spring boot
程序员cxuan2 小时前
为啥 Blender 突然火了?
人工智能·后端·程序员
IT_陈寒2 小时前
React的状态更新竟然不是同步的?!坑了我一整天
前端·人工智能·后端
卓怡学长2 小时前
w236基于springboot应用型本科院校共享在线考试系统
java·spring boot·spring·intellij-idea
必须会一定会2 小时前
Spring Boot 3 + PostgreSQL 场景工程持久化:revision、contentHash 与历史回滚
人工智能·spring boot·后端·postgresql·ai编程
object not found2 小时前
Nuxt4去掉body中默认的边距
开发语言·后端·rust
zzzll11114 小时前
Spring Boot 实现数据脱敏:自定义注解 + Jackson 序列化器
java·spring boot·后端
vx-Biye_Design9 小时前
SSM伴侣动物伴护星小程序06330-计算机课程设计、毕业设计
spring boot·后端·elasticsearch·小程序·架构·课程设计·idea