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.

Java Tech Workshop
Java Tech Workshop
Java Tech Workshop
Spring Boot 4.0 + gRPC: Minimal Integration in 4 Steps, Faster Than REST

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.GlobalGrpcInterceptor

3.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: 5000ms

4.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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaMicroservicesgRPCProtobufSpring Bootgrpc-spring-boot-starter
Java Tech Workshop
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.