Wer in den letzten Jahren gRPC mit Spring Boot nutzen wollte, musste oft auf Community-Starter zurückgreifen. Mit Spring Boot 4 ändert sich das Spiel: gRPC ist nun ein First-Class Citizen. In diesem Guide schauen wir uns an, wie du ein Greenfield-Projekt mit Java aufsetzt und warum Virtual Threads die Art und Weise, wie wir gRPC-Services schreiben, fundamental verändern.
Warum gRPC in 2026?
REST ist nach wie vor der Standard für öffentliche APIs. Aber im Maschinenraum deiner Microservices – der Inter-Service-Kommunikation – zählt Effizienz. gRPC bietet:
- Protobuf: Binäre Serialisierung statt aufgeblähtem JSON.
- HTTP/2: Multiplexing und Header-Kompression standardmäßig.
- Typsicherheit: Der Vertrag (
.proto) generiert den Code. Keine manuellen DTOs mehr, die aus dem Ruder laufen.
Das Szenario: Inventory-Service
Wir bauen einen klassischen Anwendungsfall: Ein Order-Service fragt beim Inventory-Service die Verfügbarkeit eines Produkts ab.
Der Vertrag: inventory.proto
Alles beginnt mit dem Interface-Design. In Spring Boot 4 legen wir diese Files standardmäßig unter src/main/proto ab.
syntax = "proto3";
option java_package = "dev.julianpaul.inventory.grpc";
option java_multiple_files = true;
service InventoryService {
// Unary: Einfache Anfrage, einfache Antwort
rpc GetStock (StockRequest) returns (StockResponse);
}
message StockRequest {
string product_id = 1;
}
message StockResponse {
int32 quantity = 1;
bool available = 2;
}
Dependencies
Dank des neuen offiziellen Starters brauchen wir kaum noch Konfiguration. Die Code-Generierung ist nun tiefer in die Spring-Tooling-Kette integriert.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-grpc'
// Virtual Threads Support ist in Boot 4 per Default aktiv,
// wenn Java 21+ genutzt wird.
}
In Spring Boot 4 ist das spring-boot-maven-plugin intelligent genug, um mit dem gRPC-Protokollpuffer umzugehen, sofern wir die passende Erweiterung hinzufügen.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-grpc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.1</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.25.1:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.62.2:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Hinweis: Achte darauf, dass deine
.protoFiles untersrc/main/protoliegen. Nach einemmvn clean compilegeneriert Maven die Java-Klassen automatisch in dentarget/generated-sourcesOrdner, den deine IDE (IntelliJ/VS Code) dann indizieren kann.
Implementierung des Servers
Hier glänzt Spring Boot 4. Wir müssen keine komplexen Server-Builder mehr manuell konfigurieren. Ein einfacher @GrpcService reicht aus.
@GrpcService
public class InventoryServiceImpl extends InventoryServiceGrpc.InventoryServiceImplBase {
@Override
public void getStock(StockRequest request, StreamObserver<StockResponse> responseObserver) {
// Dank Virtual Threads in SB4 können wir hier blockierende
// DB-Calls (z.B. JPA) ohne Performance-Einbußen machen!
int stockCount = 42; // Simulierter DB-Call
StockResponse response = StockResponse.newBuilder()
.setQuantity(stockCount)
.setAvailable(stockCount > 0)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
Tip: In Spring Boot 4 sind Virtual Threads standardmäßig aktiviert (
spring.threads.virtual.enabled=true). Das bedeutet, jeder gRPC-Call läuft in seinem eigenen Virtual Thread. Du musst dich nicht mehr mit reaktiven Flux/Mono-Ketten quälen, um Skalierbarkeit zu erreichen. Schreib einfach imperativen Code, er skaliert trotzdem.
Die Client-Seite: Inter-Service Communication
Jetzt, wo unser Server läuft, muss der Order-Service ihn aufrufen. In Spring Boot 4 geschieht dies über eine deklarative Injection. Wir müssen uns nicht mehr um das manuelle Erstellen von ManagedChannels kümmern.
Der gRPC Client im Order-Service
In der application.yaml konfigurieren wir das Ziel:
spring:
threads:
virtual:
enabled: true
grpc:
client:
inventory-service:
address: 'static://localhost:9090'
negotiation-type: plaintext # Für lokale Entwicklung ohne TLS
Und so sieht der Aufruf im Java-Code aus:
@Service
public class OrderService {
// Spring Boot 4 injiziert den Stub automatisch basierend auf der Config
@GrpcClient("inventory-service")
private InventoryServiceGrpc.InventoryServiceBlockingStub inventoryStub;
public void processOrder(String productId) {
StockRequest request = StockRequest.newBuilder()
.setProductId(productId)
.build();
// Der Call blockiert den Virtual Thread, aber nicht den Plattform-Thread!
StockResponse response = inventoryStub.getStock(request);
if (response.getAvailable()) {
System.out.println("Artikel verfügbar: " + response.getQuantity());
}
}
}
Observability: Keine Blindflüge mehr
Wenn der Order-Service den Inventory-Service via gRPC aufruft, wollen wir genau sehen, wo die Millisekunden bleiben. Spring Boot 4 nutzt hierfür nativ das OpenTelemetry (OTLP) Protokoll.
Die neue „One-Stop“ Dependency
Vergiss die Zeiten, in denen du Tracing, Metrics und Logs einzeln konfigurieren musstest. In Spring Boot 4 reicht ein einziger Starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-opentelemetry</artifactId>
</dependency>
Dank der nativen Integration von Micrometer Observation werden gRPC-Calls automatisch instrumentiert. Ein gRPC-Server-Interzeptor registriert Spans für jeden eingehenden Call, und der Client tut dasselbe für ausgehende Requests.
Zero-Config Tracing
In der application.yaml musst du nur noch sagen, wohin die Daten fließen sollen (z.B. an einen Jaeger- oder Grafana-Tempo-Collector):
management:
otlp:
tracing:
endpoint: http://otel-collector:4317
tracing:
sampling:
probability: 1.0 # In Produktion eher 0.1
Deep Dive: Das Geniale an Spring Boot 4 ist der Bridge-Effekt. Die
Micrometer Observation APIfungiert als gemeinsames Interface. Wenn du eine Methode mit@Observedmarkierst, erstellt Spring daraus gleichzeitig einen Trace-Span für Tempo und ein Metric-Sample für Prometheus.
Das Zusammenspiel mit Virtual Threads
Hier schließt sich der Kreis zu unserem Tech-Stack: Da wir Virtual Threads nutzen, ist das Context Propagation Thema (also das Mitführen der Trace-ID über Thread-Grenzen hinweg) in Spring Boot 4 endlich stabil gelöst. Du musst keine speziellen ThreadLocal-Hacks mehr schreiben; der Trace-Context “wandert” einfach mit dem Virtual Thread mit.
Absolut richtig. Wer schon mal ratlos vor einem INTERNAL: Unknown error saß, weiß: Ohne sauberes Error Handling ist gRPC im produktiven Einsatz ein Albtraum. Da gRPC nicht auf HTTP-Statuscodes wie 404 oder 500 basiert, sondern auf eigenen gRPC Status Codes, müssen wir hier präzise sein.
In Spring Boot 4 nutzen wir dafür den Global Interceptor Ansatz oder – noch eleganter – den neuen @GrpcExceptionHandler.
Error Handling: Schluss mit “Unknown Error”
In der REST-Welt nutzen wir @ControllerAdvice. In Spring Boot 4 mit gRPC gibt es ein analoges Konzept, um Exceptions in gRPC-Statuscodes zu übersetzen.
Der deklarative Ansatz
Statt in jedem Service try-catch Blöcke zu schreiben, definieren wir eine zentrale Komponente:
@GrpcAdvice
public class GlobalGrpcExceptionHandler {
@GrpcExceptionHandler(EntityNotFoundException.class)
public Status handleNotFound(EntityNotFoundException ex) {
return Status.NOT_FOUND
.withDescription(ex.getMessage())
.withCause(ex);
}
@GrpcExceptionHandler(IllegalArgumentException.class)
public Status handleInvalidArgument(IllegalArgumentException ex) {
return Status.INVALID_ARGUMENT
.withDescription("Check your input: " + ex.getMessage());
}
}
Rich Error Model (Google RPC Status)
Manchmal reicht ein einfacher Statuscode nicht aus. Wenn wir z.B. Validierungsfehler mitsenden wollen, nutzen wir das Rich Error Model. Spring Boot 4 unterstützt dies nativ über Metadaten (Trailers).
Tip: Nutze niemals
Status.INTERNALfür Business-Logik-Fehler. Das signalisiert dem Client meistens, dass der Server abgestürzt ist. Nutze stattdessen spezifische Codes wieFAILED_PRECONDITIONoderOUT_OF_RANGE, um dem Aufrufer mitzuteilen, was inhaltlich schiefgelaufen ist.
Fazit: Warum Spring Boot 4 ein Gamechanger für gRPC ist
Wir haben gesehen, dass gRPC im Jahr 2026 keine “Nischen-Technologie” mehr für High-Frequency-Trading ist. Durch die native Integration in Spring Boot 4 wird es zur echten Alternative für jeden internen Microservice-Call.
Die Key Takeaways:
- Offizieller Support: Kein Basteln mit Drittanbieter-Startern mehr.
- Virtual Threads: Maximale Skalierbarkeit bei gewohntem, synchronem Programmiermodell.
- Observability: Tracing und Metriken sind dank OpenTelemetry out-of-the-box dabei.
Testing: Verlässliche gRPC-Suiten ohne Overhead
Niemand möchte einen kompletten Server auf Port 9090 starten, nur um eine Business-Logik zu validieren. In Spring Boot 4 nutzen wir den In-Process-Server, der die Kommunikation im Speicher abwickelt.
Unit Testing mit @GrpcTest
Der neue @GrpcTest-Slicing-Annotation erlaubt es uns, nur den gRPC-Layer zu laden – ohne den gesamten Application-Context.
@GrpcTest
@ContextConfiguration(classes = InventoryServiceImpl.class)
class InventoryServiceTest {
@GrpcClient("test")
private InventoryServiceGrpc.InventoryServiceBlockingStub inventoryStub;
@Test
void shouldReturnStockWhenProductExists() {
StockRequest request = StockRequest.newBuilder()
.setProductId("PROD-123")
.build();
StockResponse response = inventoryStub.getRawStock(request);
assertThat(response.getAvailable()).isTrue();
assertThat(response.getQuantity()).isEqualTo(42);
}
}
Integration Testing mit Testcontainers
Für echte End-to-End-Tests in einer Microservice-Landschaft nutzen wir Testcontainers. Seit 2026 gibt es verbesserte Module, die gRPC-Health-Checks nativ unterstützen, um sicherzustellen, dass der Container bereit ist, bevor der Test startet.
Tip: gRPC-Services lassen sich hervorragend mit BloomRPC oder Postman (das seit v10 gRPC nativ unterstützt) manuell testen. Für automatisierte Contract-Tests empfehle ich jedoch Pact, um sicherzustellen, dass Änderungen an der
.proto-Datei denOrder-Servicenicht lautlos zerbrechen.
Zusammenfassung: Der Gold-Standard 2026
Wir haben den Kreis geschlossen:
- Definition via Protobuf für maximale Typsicherheit.
- Effizienz durch HTTP/2 und Binär-Payloads.
- Skalierbarkeit dank Java Virtual Threads in Spring Boot 4.
- Sicherheit durch globales Error Handling und Observability.
- Stabilität durch In-Process-Testing.
