Spring Boot 4.0 + gRPC: Minimal Integration in 4 Steps, Faster Than REST
This tutorial shows how to integrate gRPC with Spring Boot 4.0 using the grpc‑spring‑boot‑starter in just four concise steps, covering a side‑by‑side comparison with REST, Maven setup, protobuf contract definition, server implementation, and client invocation with streaming support.
gRPC vs REST comparison
Transport & serialization : REST uses text‑based JSON, which repeats field names and incurs high CPU cost for (de)serialization. gRPC uses binary Protobuf, reducing payload size by 30‑70% and accelerating encode/decode by several times.
Underlying protocol : REST relies on HTTP/1.1 with a separate connection per request. gRPC runs on HTTP/2, enabling multiplexed streams over a single connection and supporting client, server, and bidirectional streaming.
Code generation : With REST developers write DTOs, controllers and documentation manually. With gRPC a .proto file is written and Maven plugins generate request/response classes, service stubs and abstract base classes, guaranteeing type‑consistency.
Built‑in capabilities : REST requires custom code for interceptors, retries, load‑balancing and rate limiting. gRPC provides these features natively.
Typical use cases : REST is suited for external APIs and front‑end interaction. gRPC excels at internal RPC, large‑scale data streaming, real‑time push and high‑throughput intra‑datacenter communication.
Environment
Spring Boot 4.0.x
grpc‑spring‑boot‑starter 2.15.0 (compatible with Spring Boot 4)
Protobuf 3.25.x
Build tool: Maven
Two modules: grpc-server (service provider) and grpc-client (consumer)
Step 1 – Unified Maven parent and Protobuf plugin
<properties>
<spring.boot.version>4.0.0</spring.boot.version>
<grpc.version>1.64.0</grpc.version>
<grpc.starter.version>2.15.0</grpc.starter.version>
<protobuf.version>3.25.3</protobuf.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- SpringBoot 4 parent -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- gRPC starter -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
<version>${grpc.starter.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-spring-boot-starter</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:${protobuf.version}:exe:${osdetector.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${osdetector.classifier}</pluginArtifact>
<protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>Step 2 – Define Protobuf contract
syntax = "proto3";
package com.grpc.user;
option java_multiple_files = true;
option java_package = "com.grpc.user.api";
option java_outer_classname = "UserProto";
option objc_class_prefix = "USER";
message UserQueryReq {
int64 userId = 1;
string userName = 2;
}
message UserQueryResp {
int64 userId = 1;
string userName = 2;
int32 age = 3;
string email = 4;
}
message BatchUserReq {
string keyword = 1;
}
message BatchUserResp {
string name = 1;
int64 id = 2;
}
service UserRpcService {
// Synchronous RPC, equivalent to a REST GET/POST
rpc getUserInfo(UserQueryReq) returns (UserQueryResp);
// Server‑side streaming for large data or real‑time push
rpc listUserStream(BatchUserReq) returns (stream BatchUserResp);
}Step 3 – Implement gRPC server
3.1 Minimal application.yml
spring:
application:
name: grpc-server-user
grpc:
server:
port: 9090
interceptors:
- com.grpc.server.interceptor.GlobalGrpcInterceptor3.2 Service implementation
import com.grpc.user.api.UserQueryReq;
import com.grpc.user.api.UserQueryResp;
import com.grpc.user.api.UserRpcServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
@GrpcService
public class UserRpcServiceImpl extends UserRpcServiceGrpc.UserRpcServiceImplBase {
@Override
public void getUserInfo(UserQueryReq request, StreamObserver<UserQueryResp> responseObserver) {
Long userId = request.getUserId();
UserQueryResp resp = UserQueryResp.newBuilder()
.setUserId(userId)
.setUserName("测试用户")
.setAge(22)
.setEmail("[email protected]")
.build();
responseObserver.onNext(resp);
responseObserver.onCompleted();
}
@Override
public void listUserStream(BatchUserReq request, StreamObserver<BatchUserResp> responseObserver) {
String keyword = request.getKeyword();
for (int i = 1; i <= 10; i++) {
BatchUserResp data = BatchUserResp.newBuilder()
.setId(i)
.setName(keyword + "_用户" + i)
.build();
responseObserver.onNext(data);
}
responseObserver.onCompleted();
}
}3.3 Global interceptor (logging, auth, timing)
import io.grpc.*;
import net.devh.boot.grpc.server.interceptor.GrpcGlobalServerInterceptor;
@GrpcGlobalServerInterceptor
public class GlobalGrpcInterceptor implements ServerInterceptor {
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
long start = System.currentTimeMillis();
ServerCall<ReqT, RespT> wrapCall = new ForwardingServerCall.SimpleForwardingServerCall<>(call) {
@Override
public void close(Status status, Metadata trailers) {
long cost = System.currentTimeMillis() - start;
System.out.println("gRPC接口耗时:" + cost + "ms,状态:" + status.getCode());
super.close(status, trailers);
}
};
return next.startCall(wrapCall, headers);
}
}3.4 Spring Boot 4 main class
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GrpcServerApplication {
public static void main(String[] args) {
SpringApplication.run(GrpcServerApplication.class, args);
}
}Running the application starts a gRPC server on port 9090 without an embedded Tomcat, keeping the process lightweight.
Step 4 – gRPC client invocation
4.1 Client application.yml
spring:
application:
name: grpc-client-demo
grpc:
client:
user-rpc-service:
address: static://127.0.0.1:9090
load-balancing-policy: round_robin
default-deadline: 5000ms4.2 Injected blocking stub and synchronous call
import com.grpc.user.api.UserQueryReq;
import com.grpc.user.api.UserQueryResp;
import com.grpc.user.api.UserRpcServiceGrpc;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class GrpcTestController {
@Resource
private UserRpcServiceGrpc.UserRpcServiceBlockingStub userRpcStub;
@GetMapping("/rpc/user")
public UserQueryResp getUser(@RequestParam Long userId) {
UserQueryReq req = UserQueryReq.newBuilder()
.setUserId(userId)
.setUserName("客户端查询")
.build();
return userRpcStub.getUserInfo(req);
}
}4.3 Streaming call example
public void streamQuery() {
BatchUserReq req = BatchUserReq.newBuilder().setKeyword("客户").build();
StreamObserver<BatchUserResp> respStream = new StreamObserver<BatchUserResp>() {
@Override
public void onNext(BatchUserResp value) {
System.out.println("流式数据:" + value.getName());
}
@Override public void onError(Throwable t) {}
@Override public void onCompleted() {
System.out.println("流式数据接收完成");
}
};
userRpcStub.listUserStream(req, respStream);
}The client injects the generated stub, calls the synchronous RPC as a local method, and can receive server‑side streamed responses via the observer.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Tech Workshop
Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
