grpc-java 常用.proto类型Java代码 demo

grpc-java 常用的.proto类型 demo 整理

博主近期在调研 istio,其中涉及需要将自己部门的 rpc 框架切换到 grpc-java 上,因此对 grpc-java 进行了相关调研,这里就对常用的.proto类型进行整理。

.proto文件中消息结构里用于定义字段的标准数据类型如下表所示,后面几列是.proto文件中定义的标准类型编译转换后在编程语言中的类型对照。
在这里插入图片描述
下面将对上面提到的 .proto 类型在 Java 代码中的使用给出代码示例
(下面的项目将在上一个示例:grpc-java springboot 同步异步调用 demo 的基础上进一步扩展)

主要包含的复杂结构:数组Mapbyte[]自定义结构枚举类型

首先是 .proto 文件:

syntax = "proto3"; //指定正在使用proto3语法,没有则默认为proto2

option java_package = "grpc.springboot.demo.api";
option java_outer_classname = "ComplexStructService";

package complex.struct.service;

service ComplexStruct {
    //定义一个rpc方法,请求跟相应的内容,均为自定义类型
    rpc invoker (ComplexStructRequest) returns (ComplexStructResponse) {}
}

//定义请求体
message ComplexStructRequest {
    //定义字符串类型
    string msg = 1;
    //定义浮点类型
    double num = 2;
    //定义long类型
    sint64 longNum = 3;
    //定义int类型
    sint32 shortNum = 4;
    //定义boolean类型
    bool check = 5;
    //定义int型数组
    repeated sint32 shortNumArray = 6;
    //定义字符串数组
    repeated string messageArray = 7;
    //定义map结构,k-v均为字符串
    map<string, string> testMap = 8;
    //定义枚举类型
    enumType enumTypeNum = 9;
    //定义字节数组
    bytes data = 10;
}

//定义响应类型
message ComplexStructResponse {
    //包含了自定义Result
    Result result = 1;
    //自定义Result的数组
    repeated Result resultList = 2;
    //自定义Result的map
    map<string, Result> resultMap = 3;
}

message Result {
    sint64 id = 1;
    string message = 2;
}

//枚举类型
enum enumType {
    ENUM_TYPE_0 = 0;
    ENUM_TYPE_1 = 1;
    ENUM_TYPE_2 = 2;
    ENUM_TYPE_3 = 3;
}

对于 List 或者是 Map,代码添加时,都可以通过 addAllXxxx 整个 List/Map 对象赋值,或者通过 addXxx 的方法,依次进行赋值。具体见客户端与服务端代码。

客户端代码:

@RestController
public class ComplexStructController {
   
   

    private static final Logger logger = LoggerFactory.getLogger(ComplexStructController.class);

    private GrpcClient client;

    @Autowired
    public ComplexStructController(GrpcClient client) {
   
   
        this.client = client;
    }

	// 这里构建了一个请求,用于触发客户端调用服务端
    @GetMapping("/complex/struct/hello")
    public String hello() {
   
   
        List<String> allMessageArray = new ArrayList<>();
        allMessageArray.add("string1");
        allMessageArray.add("string2");
        allMessageArray.add("string3");

        Map<String, String> testMap = new HashMap<>();
        testMap.put("key1", "value1");
        testMap.put("key2", "value2");

        String string = "hello world";
        byte[] bytes = string.getBytes();

        ComplexStructService.ComplexStructRequest request = ComplexStructService.ComplexStructRequest
                .newBuilder()
                // 添加基础的数据类型
                .setMsg(
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.rain4cloud</groupId> <version>1.0-SNAPSHOT</version> <artifactId>grpc-api</artifactId> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- 这里不需要手动设置 os.detected.classifier,插件会自动检测 --> <!--<os.detected.classifier>windows-x86_64</os.detected.classifier>--> <!--mac M暂未兼容,使用x86--> <os.detected.classifier>osx-x86_64</os.detected.classifier> <protobuf-maven-plugin.version>0.6.1</protobuf-maven-plugin.version> <protoc.version>3.25.3</protoc.version> <protobuf.java.version>3.25.3</protobuf.java.version> <grpc-java.version>1.70.0</grpc-java.version> </properties> <dependencies> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.70.0</version> <exclusions> <exclusion> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.70.0</version> <exclusions> <exclusion> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-api</artifactId> <version>1.70.0</version> </dependency> <!-- javax.annotation API --> <!-- <dependency>--> <!-- <groupId>javax.annotation</groupId>--> <!-- <artifactId>javax.annotation-api</artifactId>--> <!-- <version>1.3.2</version> <!– 使用最新版本或适合你项目的版本 –>--> <!-- </dependency>--> <!----> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>3.0.0</version> </dependency> </dependencies> <build> <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.3:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.70.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>com.google.code.maven-replacer-plugin</groupId> <artifactId>replacer</artifactId> <version>1.5.3</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>replace</goal> </goals> </execution> </executions> <configuration> <includes> <include>**/*.java</include> </includes> <replacements> <replacement> <token>javax.annotation.Generated</token> <value>jakarta.annotation.Generated</value> </replacement> </replacements> </configuration> </plugin> </plugins> </build> </project>
最新发布
07-16
<think>我们之前已经讨论了在gRPC 1.70.0中生成的代码仍然使用`javax.annotation`注解的问题,并提供了两种解决方案。现在用户可能已经尝试了这些方案(比如排除`javax.annotation`依赖并引入`jakarta.annotation`,以及使用`grpc-api-jakarta`),但发现生成的代码中仍然有`javax.annotation.Generated`的导入。用户希望彻底移除或替换这些引用。 问题核心:即使我们通过依赖管理排除了`javax.annotation`,生成的代码(由`protoc-gen-grpc-java`生成)在源码级别仍然使用`javax.annotation`。这是因为代码生成器(protoc插件)在生成代码时固定使用了`javax.annotation`。 在gRPC Java的1.70.0版本中,官方还没有提供生成`jakarta.annotation`的选项。因此,我们只能通过后处理的方式(例如使用Maven/Gradle插件在生成代码后替换字符串)来解决。 下面我们提供一种更可靠的替换方法,使用Maven的Antrun插件在生成代码后立即进行替换,这样可以确保在编译之前代码已经被修改。 具体步骤: 1. 在Maven构建过程中,当protobuf插件生成Java代码后,使用Antrun插件执行一个替换任务,将生成的Java文件中的`javax.annotation.Generated`替换为`jakarta.annotation.Generated`。 2. 同时,确保依赖中已经排除了`javax.annotation`并引入了`jakarta.annotation`。 示例Maven配置: 首先,配置protobuf插件生成代码(略过标准配置,假设已经配置好)。 然后,在protobuf插件执行后,使用Antrun插件进行替换: ```xml <build> <plugins> <!-- 配置protobuf-maven-plugin生成代码 --> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <!-- 配置protoc和插件路径等 --> </configuration> <executions> <execution> <id>generate-grpc-java</id> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> <!-- 使用maven-antrun-plugin进行替换 --> <plugin> <artifactId>maven-antrun-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <phase>generate-sources</phase> <configuration> <target> <!-- 替换所有生成的Grpc类中的注解包 --> <replace dir="${project.build.directory}/generated-sources/protobuf/grpc-java" includes="**/*.java" token="javax.annotation.Generated" value="jakarta.annotation.Generated"/> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>org.apache.ant</groupId> <artifactId>ant</artifactId> <version>1.10.12</version> </dependency> <dependency> <groupId>org.apache.ant</groupId> <artifactId>ant-commons-net</artifactId> <version>1.10.12</version> </dependency> </dependencies> </plugin> </plugins> </build> ``` 注意:上面的`replace`任务需要Ant版本支持,并且确保替换的目录正确(即protobuf插件生成grpc-java代码的目录)。 3. 依赖配置排除`javax.annotation`并引入`jakarta.annotation`: ```xml <dependencies> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.70.0</version> <exclusions> <exclusion> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-api-jakarta</artifactId> <version>1.70.0</version> </dependency> </dependencies> ``` 这样,在生成代码后立即替换,然后编译的代码就是使用`jakarta.annotation`的版本了。 另外,如果用户使用的是Gradle,也可以使用类似的任务,在生成代码后添加一个任务进行替换。 但是,请注意:这种方法修改了生成的代码,如果重新生成代码(比如清理后重新构建),替换步骤会再次执行,因此不会影响后续的构建。 如果用户不想修改生成的代码,那么只能等待gRPC官方提供生成`jakarta.annotation`的支持。根据gRPC的路线图,预计在1.80+版本会支持(参考之前提到的Issue #9386)。 因此,目前这是一个可行的解决方案。 最后,验证替换是否成功:在构建后,检查`target/generated-sources/protobuf/grpc-java`目录下的Grpc类文件,应该已经替换为`jakarta.annotation.Generated`。 如果用户遇到其他问题,比如替换任务没有执行,请检查生成代码的目录是否正确,以及Antrun插件是否在`generate-sources`阶段执行。 相关问题: 1. 如何验证生成的gRPC代码中已经使用了Jakarta注解? 2. 在Gradle中如何实现类似的替换? 3. 如果生成的代码中还有其他`javax.annotation`的引用(如`@javax.annotation.Nullable`),该如何处理?</think>### 解决 gRPC 1.70.0 生成代码中 `javax.annotation` 残留问题 尽管已排除依赖并引入 Jakarta 适配层,但生成的代码仍包含 `javax.annotation` 引用,这是因为 **gRPC 代码生成器 (`protoc-gen-grpc-java`)** 在源码级别硬编码了该引用。以下是彻底解决方案: --- ### 终极解决方案:构建时注解替换 #### Maven 配置(推荐) ```xml <build> <plugins> <!-- 1. 使用 protobuf-maven-plugin 生成代码 --> <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.70.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals><goal>compile</goal><goal>compile-custom</goal></goals> </execution> </executions> </plugin> <!-- 2. 在编译前替换注解 --> <plugin> <groupId>com.google.code.maven-replacer-plugin</groupId> <artifactId>replacer</artifactId> <version>1.5.3</version> <executions> <execution> <phase>generate-sources</phase> <goals><goal>replace</goal></goals> </execution> </executions> <configuration> <includes> <include>**/*Grpc.java</include> <!-- 目标文件 --> </includes> <replacements> <!-- 替换 Generated 注解 --> <replacement> <token>javax.annotation.Generated</token> <value>jakarta.annotation.Generated</value> </replacement> <!-- 替换 Nullable 注解 --> <replacement> <token>javax.annotation.Nullable</token> <value>org.jetbrains.annotations.Nullable</value> <!-- 或 jakarta.annotation.Nullable --> </replacement> </replacements> </configuration> </plugin> </plugins> </build> <!-- 3. 依赖配置 --> <dependencies> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.70.0</version> <exclusions> <exclusion> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-api-jakarta</artifactId> <version>1.70.0</version> </dependency> </dependencies> ``` --- ### 关键原理说明 1. **问题根源** gRPC 代码生成器 (`protoc-gen-grpc-java`) 在 1.70.0 版本中**源码级硬编码**了 `javax.annotation` 引用: ```java // 生成的 *Grpc.java 文件 import javax.annotation.Generated; ``` 即使排除依赖,生成的代码仍包含这些导入[^1]。 2. **解决方案有效性** - 通过 `replacer` 插件在 **代码生成后、编译前** 直接修改源文件 - 替换范围精确到 `*Grpc.java` 文件(服务存根类) - 同时处理 `@Generated` 和 `@Nullable` 等注解 3. **验证方法** 检查编译后的类文件是否包含 Jakarta 注解: ```bash # 查看编译后的类文件 unzip -l target/classes/**/*Grpc*.class | grep 'Generated' # 应输出: jakarta/annotation/Generated ``` --- ### 替代方案:Gradle 实现 ```groovy // build.gradle plugins { id "com.google.protobuf" version "0.9.4" } dependencies { implementation 'io.grpc:grpc-stub:1.70.0' implementation 'jakarta.annotation:jakarta.annotation-api:2.1.1' implementation 'io.grpc:grpc-api-jakarta:1.70.0' } protobuf { protoc { artifact = "com.google.protobuf:protoc:3.25.1" } plugins { grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.70.0" } } generateProtoTasks { all().each { task -> task.finalizedBy replaceJavaxAnnotations } } } // 注册替换任务 task replaceJavaxAnnotations(type: Copy) { from("$buildDir/generated/source/proto/main/grpc") into("$buildDir/generated/source/proto/main/grpc") filter { line -> line.replace('javax.annotation.Generated', 'jakarta.annotation.Generated') .replace('javax.annotation.Nullable', 'jakarta.annotation.Nullable') } } ``` --- ### 官方支持进展 - **预计 2024 年底** 的 gRPC 1.80+ 版本将原生支持 Jakarta[^1] - 当前临时方案可稳定过渡: ```mermaid timeline title gRPC Jakarta 支持路线 2023 Q4 : 1.70.0 (当前) 2024 Q2 : 1.75+ (实验性支持) 2024 Q4 : 1.80+ (正式支持) ``` --- ### 常见问题排查 1. **替换后编译报错** 检查是否遗漏其他注解(如 `@Nullable`),在替换规则中补充 2. **生成代码目录不匹配** 确认 `replacer` 插件的 `<include>` 路径与实际生成路径一致: ```xml <!-- 典型路径 --> <include>target/generated-sources/protobuf/**/*.java</include> ``` 3. **Spring Boot 3 兼容性** 结合 `grpc-spring-boot-starter` 时需确保版本兼容: ```xml <dependency> <groupId>net.devh</groupId> <artifactId>grpc-spring-boot-starter</artifactId> <version>2.15.0.RELEASE</version> <!-- 支持 Boot 3.x --> </dependency> ``` --- ### 相关问题 1. 如何验证 gRPC 生成的代码是否完全兼容 Jakarta EE 9+ 规范? 2. 在 Spring Boot 3 中集成 gRPC 服务的最佳实践是什么? 3. 使用 Protocol Buffers 和 gRPC 时如何避免版本冲突? [^1]: Spring Boot 集成 gRPC 快速入门 demo 展示了基础配置方法 [^2]: 使用 Protocol Buffers 编译时需注意插件版本兼容性 [^3]: gRPC 代码生成器从 `protoc-gen-go` 到 `protoc-gen-go-grpc` 的演进
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值