Add support for registering service definition bean

This commit is contained in:
Dave Syer
2024-11-06 11:51:29 +00:00
parent 7fed50a420
commit f0ec4b1bac
4 changed files with 31 additions and 7 deletions

View File

@@ -5,12 +5,15 @@ This section describes core concepts that Spring gRPC uses on the server side. W
You only need to provide one or more beans of type `BindableService` to create a gRPC server, provided the classpath contains an implementation of a `Server`. The `BindableService` is a gRPC service that can be bound to a server.
The `Server` is the gRPC server that listens for incoming requests and routes them to the appropriate service implementation.
== Create a `BindableService`
== Create a gRPC Service
To create a gRPC server, you need to provide one or more beans of type `BindableService`.
To create a gRPC server, you need to provide one or more beans of type `BindableService` or `ServerServiceDefinition`.
There are some `BindableServices` available off the shelf that you could include in your application (an example is the reflection service from the `grpc-services` artifact which allows clients to browse the metadata of your services and download the Portobuf files).
Very commonly, you will create your own `BindableService` by extending the generated service implementation from your Protobuf file.
The easiest way to do it is to simply add a Spring `@Service` annotation to the implementation class and have it picked up by the `@ComponentScan` in your Spring Boot application.
The easiest way to activate it is to simply add a Spring `@Service` annotation to the implementation class and have it picked up by the `@ComponentScan` in your Spring Boot application.
Alternatively, you could create an instance of it in a `@Configuration` class and return it as a bean, or return it's `ServerServiceDefinition`.
The last option is useful if you need to add specific behaviour, like an interceptor, to the service.
It is an error to have more than one instance of the same service name, so don't create both a `BindableService` and a `ServerServiceDefinition` bean for the same service.
== Netty Server

View File

@@ -16,6 +16,7 @@
package org.springframework.grpc.autoconfigure.server;
import java.util.ArrayList;
import java.util.List;
import io.grpc.BindableService;
@@ -35,13 +36,20 @@ class DefaultGrpcServiceDiscoverer implements GrpcServiceDiscoverer {
private final ObjectProvider<BindableService> grpcServicesProvider;
DefaultGrpcServiceDiscoverer(ObjectProvider<BindableService> grpcServicesProvider) {
private ObjectProvider<ServerServiceDefinition> grpcServiceDefinitionsProvider;
DefaultGrpcServiceDiscoverer(ObjectProvider<BindableService> grpcServicesProvider,
ObjectProvider<ServerServiceDefinition> grpcServiceDefinitionsProvider) {
this.grpcServicesProvider = grpcServicesProvider;
this.grpcServiceDefinitionsProvider = grpcServiceDefinitionsProvider;
}
@Override
public List<ServerServiceDefinition> findServices() {
return grpcServicesProvider.orderedStream().map(BindableService::bindService).toList();
List<ServerServiceDefinition> list = new ArrayList<>(
grpcServicesProvider.orderedStream().map(BindableService::bindService).toList());
list.addAll(grpcServiceDefinitionsProvider.orderedStream().toList());
return list;
}
}

View File

@@ -36,6 +36,7 @@ import io.grpc.BindableService;
import io.grpc.CompressorRegistry;
import io.grpc.DecompressorRegistry;
import io.grpc.ServerBuilder;
import io.grpc.ServerServiceDefinition;
/**
* {@link EnableAutoConfiguration Auto-configuration} for gRPC server-side components.
@@ -75,8 +76,9 @@ public class GrpcServerAutoConfiguration {
@ConditionalOnMissingBean
@Bean
GrpcServiceDiscoverer grpcServiceDiscoverer(ObjectProvider<BindableService> bindableServicesProvider) {
return new DefaultGrpcServiceDiscoverer(bindableServicesProvider);
GrpcServiceDiscoverer grpcServiceDiscoverer(ObjectProvider<BindableService> bindableServicesProvider,
ObjectProvider<ServerServiceDefinition> serviceDefinitionsProvider) {
return new DefaultGrpcServiceDiscoverer(bindableServicesProvider, serviceDefinitionsProvider);
}
@ConditionalOnBean(CompressorRegistry.class)

View File

@@ -134,6 +134,17 @@ class GrpcServerAutoConfigurationTests {
.containsExactly(this.serviceDefinition));
}
@Test
void grpcServiceDiscovererWithServiceDefinitionAsExpected() {
ServerServiceDefinition serviceDefinition = ServerServiceDefinition.builder("my-other-service").build();
this.contextRunnerWithLifecyle()
.withBean(ServerServiceDefinition.class, () -> serviceDefinition)
.run((context) -> assertThat(context).getBean(GrpcServiceDiscoverer.class)
.extracting(GrpcServiceDiscoverer::findServices,
InstanceOfAssertFactories.list(ServerServiceDefinition.class))
.containsExactly(this.serviceDefinition, serviceDefinition));
}
@Test
void whenHasUserDefinedServerBuilderCustomizersDoesNotAutoConfigureBean() {
ServerBuilderCustomizers customCustomizers = mock(ServerBuilderCustomizers.class);