list = Lists.newArrayList();
+ for (final Class extends ClientInterceptor> interceptorClass : annotation.interceptors()) {
+ final ClientInterceptor clientInterceptor;
+ if (this.applicationContext.getBeanNamesForType(interceptorClass).length > 0) {
+ clientInterceptor = this.applicationContext.getBean(interceptorClass);
+ } else {
+ try {
+ clientInterceptor = interceptorClass.getConstructor().newInstance();
+ } catch (final Exception e) {
+ throw new BeanCreationException("Failed to create interceptor instance", e);
+ }
+ }
+ list.add(clientInterceptor);
+ }
+ for (final String interceptorName : annotation.interceptorNames()) {
+ list.add(this.applicationContext.getBean(interceptorName, ClientInterceptor.class));
+ }
+ return list;
+ }
+
+ private DaprClient getDaprClient() {
+ if (this.daprClient == null) {
+ this.daprClient = this.applicationContext.getBean(DaprClient.class);
+ }
+ return this.daprClient;
+ }
+}
diff --git a/dapr-spring/dapr-spring-invoke/src/main/java/io/dapr/spring/invoke/grpc/client/DaprGrpcClient.java b/dapr-spring/dapr-spring-invoke/src/main/java/io/dapr/spring/invoke/grpc/client/DaprGrpcClient.java
new file mode 100644
index 0000000000..427fa9329b
--- /dev/null
+++ b/dapr-spring/dapr-spring-invoke/src/main/java/io/dapr/spring/invoke/grpc/client/DaprGrpcClient.java
@@ -0,0 +1,71 @@
+package io.dapr.spring.invoke.grpc.client;
+
+import io.grpc.Channel;
+import io.grpc.ClientInterceptor;
+import io.grpc.stub.AbstractStub;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation for fields of type {@link Channel} or subclasses of {@link AbstractStub}/gRPC client services.
+ *
+ *
+ * Note: Fields that are annotated with this annotation should NOT be annotated with
+ * {@link Autowired} (conflict).
+ *
+ *
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface DaprGrpcClient {
+
+ /**
+ * The name of the target service. The channel use DaprClient's grpc channel.
+ *
+ *
+ * Example: @DaprGrpcClient("exampleService")
<->
+ * {@code headers.put(Metadata.Key.of("dapr-app-id", Metadata.ASCII_STRING_MARSHALLER), name); }
+ *
+ *
+ * @return The name of the target service.
+ */
+ String value();
+
+ /**
+ * A list of {@link ClientInterceptor} classes that should be used with this client in addition to the globally
+ * defined ones. If a bean of the given type exists, it will be used; otherwise a new instance of that class will be
+ * created via no-args constructor.
+ *
+ * @return A list of ClientInterceptor classes that should be used.
+ */
+ Class extends ClientInterceptor>[] interceptors() default {};
+
+ /**
+ * A list of {@link ClientInterceptor} beans that should be used with this client in addition to the globally
+ * defined ones.
+ *
+ * @return A list of ClientInterceptor beans that should be used.
+ */
+ String[] interceptorNames() default {};
+
+ /**
+ * Whether the custom ClientInterceptor defined by interceptors and interceptorNames should be sorted.
+ *
+ *
+ * Sorted by
+ * {@code interceptors.sort(AnnotationAwareOrderComparator.INSTANCE)}
+ *
+ *
+ *
+ * @return True, if the custom interceptors will be sorted. False otherwise.
+ */
+ boolean sortInterceptors() default false;
+}
diff --git a/dapr-spring/pom.xml b/dapr-spring/pom.xml
index fe4ebaa172..820c8a5765 100644
--- a/dapr-spring/pom.xml
+++ b/dapr-spring/pom.xml
@@ -19,6 +19,7 @@
dapr-spring-data
+ dapr-spring-invoke
dapr-spring-messaging
dapr-spring-workflows
dapr-spring-boot-autoconfigure
diff --git a/spring-boot-examples/consumer-app/pom.xml b/spring-boot-examples/consumer-app/pom.xml
index b2e2d09406..9e50f21888 100644
--- a/spring-boot-examples/consumer-app/pom.xml
+++ b/spring-boot-examples/consumer-app/pom.xml
@@ -13,6 +13,10 @@
Spring Boot, Testcontainers and Dapr Integration Examples :: Consumer App
+
+ io.dapr
+ grpc-lib
+
org.springframework.boot
spring-boot-starter-web
@@ -62,6 +66,13 @@
org.springframework.boot
spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
org.apache.maven.plugins
diff --git a/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/invoke/grpc/HelloWorldController.java b/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/invoke/grpc/HelloWorldController.java
new file mode 100644
index 0000000000..c109d38383
--- /dev/null
+++ b/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/invoke/grpc/HelloWorldController.java
@@ -0,0 +1,22 @@
+package io.dapr.springboot.examples.consumer.invoke.grpc;
+
+import io.dapr.examples.DaprExamplesProtos;
+import io.dapr.examples.HelloWorldGrpc;
+import io.dapr.spring.invoke.grpc.client.DaprGrpcClient;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class HelloWorldController {
+
+ @DaprGrpcClient("producer-app")
+ private HelloWorldGrpc.HelloWorldBlockingStub blockingStub;
+
+ @RequestMapping("/hello")
+ public String sayHello(@RequestParam("name") String name) {
+ DaprExamplesProtos.HelloRequest request = DaprExamplesProtos.HelloRequest.newBuilder().setName(name).build();
+ DaprExamplesProtos.HelloReply reply = blockingStub.sayHello(request);
+ return reply.getMessage();
+ }
+}
diff --git a/spring-boot-examples/grpc-lib/pom.xml b/spring-boot-examples/grpc-lib/pom.xml
new file mode 100644
index 0000000000..2e469c98e1
--- /dev/null
+++ b/spring-boot-examples/grpc-lib/pom.xml
@@ -0,0 +1,106 @@
+
+
+ 4.0.0
+
+ io.dapr
+ spring-boot-examples
+ 0.15.0-SNAPSHOT
+
+
+ grpc-lib
+ grpc-lib
+ protobuf files
+
+
+ ${project.build.directory}/generated-sources
+ ${project.basedir}/src/main/proto
+
+
+
+
+ io.grpc
+ grpc-protobuf
+ ${grpc.version}
+
+
+ io.grpc
+ grpc-stub
+ ${grpc.version}
+
+
+ io.grpc
+ grpc-api
+ ${grpc.version}
+
+
+ com.google.protobuf
+ protobuf-java
+ ${protobuf.version}
+
+
+ com.google.protobuf
+ protobuf-java-util
+ ${protobuf.version}
+
+
+ javax.annotation
+ javax.annotation-api
+ 1.3.2
+
+
+
+
+
+
+ com.github.os72
+ protoc-jar-maven-plugin
+ 3.11.4
+
+
+ generate-sources
+
+ run
+
+
+ ${protobuf.version}
+ inputs
+ direct
+ true
+
+ ${protobuf.input.directory}
+
+
+
+ java
+ ${protobuf.output.directory}
+
+
+ grpc-java
+ ${protobuf.output.directory}
+ io.grpc:protoc-gen-grpc-java:${grpc.version}
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+
+ true
+
+
+
+
+
+
diff --git a/spring-boot-examples/grpc-lib/src/main/proto/helloworld.proto b/spring-boot-examples/grpc-lib/src/main/proto/helloworld.proto
new file mode 100644
index 0000000000..d5c37bae13
--- /dev/null
+++ b/spring-boot-examples/grpc-lib/src/main/proto/helloworld.proto
@@ -0,0 +1,23 @@
+syntax = "proto3";
+
+package daprexamples;
+
+option java_outer_classname = "DaprExamplesProtos";
+option java_package = "io.dapr.examples";
+
+// User Code definitions
+service HelloWorld {
+ // 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;
+}
\ No newline at end of file
diff --git a/spring-boot-examples/pom.xml b/spring-boot-examples/pom.xml
index 75a32364f7..958ae89caa 100644
--- a/spring-boot-examples/pom.xml
+++ b/spring-boot-examples/pom.xml
@@ -19,6 +19,7 @@
+ grpc-lib
producer-app
consumer-app
@@ -30,7 +31,11 @@
spring-boot-dependencies
${springboot.version}
pom
- import
+ import
+
+ io.dapr
+ grpc-lib
+ 0.15.0-SNAPSHOT
diff --git a/spring-boot-examples/producer-app/pom.xml b/spring-boot-examples/producer-app/pom.xml
index b9ab6fcbfa..2f952c316e 100644
--- a/spring-boot-examples/producer-app/pom.xml
+++ b/spring-boot-examples/producer-app/pom.xml
@@ -14,6 +14,10 @@
Spring Boot, Testcontainers and Dapr Integration Examples :: Producer App
+
+ io.dapr
+ grpc-lib
+
org.springframework.boot
spring-boot-starter-actuator
@@ -30,6 +34,11 @@
io.dapr.spring
dapr-spring-boot-starter
+
+ net.devh
+ grpc-server-spring-boot-starter
+ 3.1.0.RELEASE
+
io.dapr.spring
dapr-spring-boot-starter-test
@@ -57,6 +66,13 @@
org.springframework.boot
spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
org.apache.maven.plugins
diff --git a/spring-boot-examples/producer-app/src/main/java/io/dapr/springboot/examples/producer/invoke/grpc/HelloWorldProducer.java b/spring-boot-examples/producer-app/src/main/java/io/dapr/springboot/examples/producer/invoke/grpc/HelloWorldProducer.java
new file mode 100644
index 0000000000..dd3d4e6aa4
--- /dev/null
+++ b/spring-boot-examples/producer-app/src/main/java/io/dapr/springboot/examples/producer/invoke/grpc/HelloWorldProducer.java
@@ -0,0 +1,19 @@
+package io.dapr.springboot.examples.producer.invoke.grpc;
+
+import io.dapr.examples.HelloWorldGrpc;
+import net.devh.boot.grpc.server.service.GrpcService;
+import org.springframework.stereotype.Service;
+
+@GrpcService
+public class HelloWorldProducer extends HelloWorldGrpc.HelloWorldImplBase {
+
+ @Override
+ public void sayHello(io.dapr.examples.DaprExamplesProtos.HelloRequest request,
+ io.grpc.stub.StreamObserver responseObserver) {
+ io.dapr.examples.DaprExamplesProtos.HelloReply reply = io.dapr.examples.DaprExamplesProtos.HelloReply.newBuilder()
+ .setMessage("Hello " + request.getName())
+ .build();
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ }
+}
From d6f59dc6273bd79d29dd12dde6f39f09bac21361 Mon Sep 17 00:00:00 2001
From: seal90 <578935869@qq.com>
Date: Sat, 12 Apr 2025 15:59:15 +0800
Subject: [PATCH 2/3] fix spotbugs warn
Signed-off-by: seal90 <578935869@qq.com>
---
.../consumer/invoke/grpc/HelloWorldController.java | 4 ++--
.../grpc-lib/src/main/proto/helloworld.proto | 2 +-
.../producer/invoke/grpc/HelloWorldProducer.java | 9 ++++-----
3 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/invoke/grpc/HelloWorldController.java b/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/invoke/grpc/HelloWorldController.java
index c109d38383..e0ff69aad8 100644
--- a/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/invoke/grpc/HelloWorldController.java
+++ b/spring-boot-examples/consumer-app/src/main/java/io/dapr/springboot/examples/consumer/invoke/grpc/HelloWorldController.java
@@ -1,7 +1,7 @@
package io.dapr.springboot.examples.consumer.invoke.grpc;
-import io.dapr.examples.DaprExamplesProtos;
-import io.dapr.examples.HelloWorldGrpc;
+import io.dapr.springboot.examples.DaprExamplesProtos;
+import io.dapr.springboot.examples.HelloWorldGrpc;
import io.dapr.spring.invoke.grpc.client.DaprGrpcClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
diff --git a/spring-boot-examples/grpc-lib/src/main/proto/helloworld.proto b/spring-boot-examples/grpc-lib/src/main/proto/helloworld.proto
index d5c37bae13..df5502640c 100644
--- a/spring-boot-examples/grpc-lib/src/main/proto/helloworld.proto
+++ b/spring-boot-examples/grpc-lib/src/main/proto/helloworld.proto
@@ -3,7 +3,7 @@ syntax = "proto3";
package daprexamples;
option java_outer_classname = "DaprExamplesProtos";
-option java_package = "io.dapr.examples";
+option java_package = "io.dapr.springboot.examples";
// User Code definitions
service HelloWorld {
diff --git a/spring-boot-examples/producer-app/src/main/java/io/dapr/springboot/examples/producer/invoke/grpc/HelloWorldProducer.java b/spring-boot-examples/producer-app/src/main/java/io/dapr/springboot/examples/producer/invoke/grpc/HelloWorldProducer.java
index dd3d4e6aa4..7ac6c7262f 100644
--- a/spring-boot-examples/producer-app/src/main/java/io/dapr/springboot/examples/producer/invoke/grpc/HelloWorldProducer.java
+++ b/spring-boot-examples/producer-app/src/main/java/io/dapr/springboot/examples/producer/invoke/grpc/HelloWorldProducer.java
@@ -1,16 +1,15 @@
package io.dapr.springboot.examples.producer.invoke.grpc;
-import io.dapr.examples.HelloWorldGrpc;
+import io.dapr.springboot.examples.HelloWorldGrpc;
import net.devh.boot.grpc.server.service.GrpcService;
-import org.springframework.stereotype.Service;
@GrpcService
public class HelloWorldProducer extends HelloWorldGrpc.HelloWorldImplBase {
@Override
- public void sayHello(io.dapr.examples.DaprExamplesProtos.HelloRequest request,
- io.grpc.stub.StreamObserver responseObserver) {
- io.dapr.examples.DaprExamplesProtos.HelloReply reply = io.dapr.examples.DaprExamplesProtos.HelloReply.newBuilder()
+ public void sayHello(io.dapr.springboot.examples.DaprExamplesProtos.HelloRequest request,
+ io.grpc.stub.StreamObserver responseObserver) {
+ io.dapr.springboot.examples.DaprExamplesProtos.HelloReply reply = io.dapr.springboot.examples.DaprExamplesProtos.HelloReply.newBuilder()
.setMessage("Hello " + request.getName())
.build();
responseObserver.onNext(reply);
From e6b3b43128db84efe223103a2353c2c8346feb59 Mon Sep 17 00:00:00 2001
From: seal90 <578935869@qq.com>
Date: Sat, 12 Apr 2025 16:17:29 +0800
Subject: [PATCH 3/3] delete author
Signed-off-by: seal90 <578935869@qq.com>
---
.../spring/invoke/grpc/client/DaprGrpcBeanPostProcessor.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/dapr-spring/dapr-spring-invoke/src/main/java/io/dapr/spring/invoke/grpc/client/DaprGrpcBeanPostProcessor.java b/dapr-spring/dapr-spring-invoke/src/main/java/io/dapr/spring/invoke/grpc/client/DaprGrpcBeanPostProcessor.java
index f631779994..50859621fc 100644
--- a/dapr-spring/dapr-spring-invoke/src/main/java/io/dapr/spring/invoke/grpc/client/DaprGrpcBeanPostProcessor.java
+++ b/dapr-spring/dapr-spring-invoke/src/main/java/io/dapr/spring/invoke/grpc/client/DaprGrpcBeanPostProcessor.java
@@ -32,7 +32,6 @@
* Handlers Dapr gRPC client in spring boot.
* Searches for fields and methods in beans that are annotated with {@link DaprGrpcClient} and sets them.
*
- * @author seal90 (578935869@qq.com)
*/
public class DaprGrpcBeanPostProcessor implements BeanPostProcessor {