Getting Started
This section describes the steps necessary to convert your application into a grpc-spring-boot-starter one.
Table of Contents
Additional Topics
- Getting started
- Configuration
- Exception Handling
- Contextual Data / Scoped Beans
- Testing the Service
- Server Events
- Security
Project Setup
Before we start adding the dependencies lets start with some of our recommendation for your project setup.
We recommend splitting your project into 2-3 separate modules.
- The interface project Contains the raw protobuf files and generates the java model and service classes. You probably share this part.
- The server project Contains the actual implementation of your project and uses the interface project as dependency.
- The client projects (optional and possibly many) Any client projects that use the pre-generated stubs to access the server.
Dependencies
Interface-Project
Maven (Interface)
<properties>
<protobuf.version>3.23.4</protobuf.version>
<protobuf-plugin.version>0.6.1</protobuf-plugin.version>
<grpc.version>1.58.0</grpc.version>
</properties>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<!-- Java 9+ compatibility - Do NOT update to 2.0.0 -->
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>1.3.5</version>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.0</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>${protobuf-plugin.version}</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Gradle (Interface)
buildscript {
ext {
protobufVersion = '3.23.4'
protobufPluginVersion = '0.8.18'
grpcVersion = '1.58.0'
}
}
plugins {
id 'java-library'
id 'com.google.protobuf' version "${protobufPluginVersion}"
}
repositories {
mavenCentral()
}
dependencies {
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
implementation "io.grpc:grpc-stub:${grpcVersion}"
compileOnly 'jakarta.annotation:jakarta.annotation-api:1.3.5' // Java 9+ compatibility - Do NOT update to 2.0.0
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:${protobufVersion}"
}
generatedFilesBaseDir = "$projectDir/src/generated"
clean {
delete generatedFilesBaseDir
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}
// Optional
eclipse {
classpath {
file.beforeMerged { cp ->
def generatedGrpcFolder = new org.gradle.plugins.ide.eclipse.model.SourceFolder('src/generated/main/grpc', null);
generatedGrpcFolder.entryAttributes['ignore_optional_problems'] = 'true';
cp.entries.add( generatedGrpcFolder );
def generatedJavaFolder = new org.gradle.plugins.ide.eclipse.model.SourceFolder('src/generated/main/java', null);
generatedJavaFolder.entryAttributes['ignore_optional_problems'] = 'true';
cp.entries.add( generatedJavaFolder );
}
}
}
// Optional
idea {
module {
sourceDirs += file("src/generated/main/java")
sourceDirs += file("src/generated/main/grpc")
generatedSourceDirs += file("src/generated/main/java")
generatedSourceDirs += file("src/generated/main/grpc")
}
}
Server-Project
Maven (Server)
<dependencies>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>example</groupId>
<artifactId>my-grpc-interface</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Gradle (Server)
apply plugin: 'org.springframework.boot'
dependencies {
compile('org.springframework.boot:spring-boot-starter')
compile('net.devh:grpc-server-spring-boot-starter')
compile('my-example:my-grpc-interface')
}
buildscript {
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
Client-Project
See the client getting started page
Creating the gRPC-Service Definitions
Place your protobuf definitions / .proto files in src/main/proto.
For writing protobuf files please refer to the official
protobuf docs.
Your .proto files will look similar to the example below:
syntax = "proto3";
package net.devh.boot.grpc.example;
option java_multiple_files = true;
option java_package = "net.devh.boot.grpc.examples.lib";
option java_outer_classname = "HelloWorldProto";
// The greeting service definition.
service MyService {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
The configured maven/gradle protobuf plugins will then use invoke the
protoc compiler with the
protoc-gen-grpc-java plugin and generate the data
classes, grpc service ImplBases and Stubs. Please note that other plugins such as
reactive-grpc might generate additional/alternative classes that you have
to use instead. However, they can be used in a similar fashion.
- The
ImplBaseclasses contain the base logic that map the dummy implementation to the grpc service methods. More about this in the Implementing the service topic. - The
Stubclasses are complete client implementations. More about this on the Getting the client started page.
Implementing the Service
The protoc-gen-grpc-java plugin generates a class for each of your grpc services.
For example: MyServiceGrpc where MyService is the name of the grpc service in the proto file. This class
contains both the client stubs and the server ImplBase that you will need to extend.
After that you have only four tasks to do:
- Make sure that your
MyServiceImplextendsMyServiceGrpc.MyServiceImplBase - Add the
@GrpcServiceannotation to yourMyServiceImplclass - Make sure that the
MyServiceImplis added to your application context,- either by creating
@Beandefinition in one of your@Configurationclasses - or placing it in spring’s automatically detected paths (e.g. in the same or a sub package of your
Mainclass)
- either by creating
- Actually implement the grpc service methods.
Your grpc service class will then look somewhat similar to the example below:
import example.HelloReply;
import example.HelloRequest;
import example.MyServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
@GrpcService
public class MyServiceImpl extends MyServiceGrpc.MyServiceImplBase {
@Override
public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
HelloReply reply = HelloReply.newBuilder()
.setMessage("Hello ==> " + request.getName())
.build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
Note: Theoretically it is not necessary to extend the
ImplBaseand instead implementBindableServiceyourself. However, doing so might result in bypassing spring security’s checks.
That’s all there is to that. Now you can start your spring-boot application and start sending requests to your grpc-service.
By default, the grpc-server will be started on port 9090 using PLAINTEXT mode.
You can test that your application is working as expected by running these gRPCurl commands:
grpcurl --plaintext localhost:9090 list
grpcurl --plaintext localhost:9090 list net.devh.boot.grpc.example.MyService
# Linux (Static content)
grpcurl --plaintext -d '{"name": "test"}' localhost:9090 net.devh.boot.grpc.example.MyService/sayHello
# Windows or Linux (dynamic content)
grpcurl --plaintext -d "{\"name\": \"test\"}" localhost:9090 net.devh.boot.grpc.example.MyService/sayHello
See here for gRPCurl example command output and additional information.
Note: Don’t forget to write actual/automated tests for your service implementation.
Additional Topics
- Getting Started
- Configuration
- Exception Handling
- Contextual Data / Scoped Beans
- Testing the Service
- Server Events
- Security