Spring Boot集成grpc快速入门demo

1.什么是GRPC?

gRPC 是一个高性能、开源、通用的RPC框架,由Google推出,基于HTTP2协议标准设计开发,默认采用Protocol Buffers数据序列化协议,支持多种开发语言。gRPC提供了一种简单的方法来精确的定义服务,并且为客户端和服务端自动生成可靠的功能库。 在gRPC客户端可以直接调用不同服务器上的远程程序,使用姿势看起来就像调用本地程序一样,很容易去构建分布式应用和服务。和很多RPC系统一样,服务端负责实现定义好的接口并处理客户端的请求,客户端根据接口描述直接调用需要的服务。客户端和服务端可以分别使用gRPC支持的不同语言实现。

grpc_concept_diagram_00

主要特性

  • 强大的IDLgRPC使用ProtoBuf来定义服务,ProtoBuf是由Google开发的一种数据序列化协议(类似于XML、JSON、hessian)。ProtoBuf能够将数据进行序列化,并广泛应用在数据存储、通信协议等方面。
  • 多语言支持gRPC支持多种语言,并能够基于语言自动生成客户端和服务端功能库。目前已提供了C版本grpc、Java版本grpc-java 和 Go版本grpc-go,其它语言的版本正在积极开发中,其中,grpc支持C、C++、Node.js、Python、Ruby、Objective-C、PHP和C#等语言,grpc-java已经支持Android开发。
  • HTTP2gRPC基于HTTP2标准设计,所以相对于其他RPC框架,gRPC带来了更多强大功能,如双向流、头部压缩、多复用请求等。这些功能给移动设备带来重大益处,如节省带宽、降低TCP链接次数、节省CPU使用和延长电池寿命等。同时,gRPC还能够提高了云端服务和Web应用的性能。gRPC既能够在客户端应用,也能够在服务器端应用,从而以透明的方式实现客户端和服务器端的通信和简化通信系统的构建。

 

2.代码工程

我们建议将您的项目分为2至3个不同的模块。

  1. grpc-api 项目 包含原始 protobuf 文件并生成 java model 和 service 类。 你可能会在不同的项目中会共享这个部分。
  2. grpc-Server 项目 包含项目的业务实现,并使用上面的 Interface 项目作为依赖项。
  3. grpc-Client 项目(可选,可能很多) 任何使用预生成的 stub 来访问服务器的客户端项目。

实验目的

定义了一个Hello Service,客户端发送包含字符串名字的请求,服务端返回Hello消息。

grpc-api

pom.xml
<?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">
    <parent>
        <artifactId>grpc</artifactId>
        <groupId>com.et</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>grpc-api</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <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>
</project>
proto

将您的 protobuf 定义/.proto文件放入src/main/proto。 有关编写 protobuf 文件的信息,请参阅官方的 protobuf 文档。 您的 .proto 文件跟如下的示例类似:

syntax = "proto3";

package net.devh.boot.grpc.example;

option java_multiple_files = true;
option java_package = "com.et.grpc.api.protobuf.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;
}

生成文件,源码在target/generated-sources/protobuf

mvn protobuf:compile
mvn protobuf:compile-custom

最后打包给server和client引用

grpc-server

protoc-gen-grpc-java 插件为你的每个 grpc 服务生成一个类。 例如:MyServiceGrpc 的 MyService 是 proto 文件中的 grpc 服务名称。 这个类 包含您需要扩展的客户端 stub 和服务端的 ImplicBase。 在这之后,你还有四个步骤:

  1. 请确保您的 MyServiceImp 实现了 MyServiceGrpc.MyServiceImpBase
  2. 将 @GrpcService 注解添加到您的 MyServiceImp 类上
  3. 请确保 MyServiceImplic 已添加到您的应用程序上下文中。
    • 通过在您的 @Configuration 类中创建 @Bean
    • 或者将其放置在 spring 的自动检测到路径中(例如在您Main类的相同或子包中)
  4. 实现 grpc 服务方法。
pom.xml
<?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">
    <parent>
        <artifactId>grpc</artifactId>
        <groupId>com.et</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>grpc-server</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-server-spring-boot-starter</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>com.et</groupId>
            <artifactId>grpc-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
service
/*
 * Copyright (c) 2016-2023 The gRPC-Spring Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.et.grpc.server;

import com.et.grpc.api.protobuf.lib.HelloReply;
import com.et.grpc.api.protobuf.lib.HelloRequest;
import com.et.grpc.api.protobuf.lib.MyServiceGrpc;
import io.grpc.stub.StreamObserver;

import net.devh.boot.grpc.server.service.GrpcService;

/**
 * @author Michael (yidongnan@gmail.com)
 * @since 2016/11/8
 */

@GrpcService
public class GrpcServerService extends MyServiceGrpc.MyServiceImplBase {

    @Override
    public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
        HelloReply reply = HelloReply.newBuilder().setMessage("Hello ==> " + req.getName()).build();
        responseObserver.onNext(reply);
        responseObserver.onCompleted();
    }

}
application.yaml
spring:
  application:
    name: local-grpc-server
grpc:
  server:
    port: 9898

grpc-client

以下列表包含您可能在客户端使用到的的所有功能。 如果您不想使用任何高级功能,那么前两个元素可能都是您需要使用的。

  • @GrpcClient: 在需要注入的客户端的字段或者 setter 方法上这个注解。 支持 Channel和各种类型的 Stub。 请不要将 @GrpcClient 与 @Autowireed 或 @Inject 一起使用。 目前,它不支持构造函数和 @Bean 方法参数。 这种情况请查看后面 @GrpcClientBean 的使用文档。 注意: 同一应用程序提供的服务只能在 ` ApplicationStartedEvent 之后访问/调用。 连接到外部服务的 Stubs 可以提前使用;从 @PostConstruct / 初始化Bean#afterPropertiesSet()` 开始。
  • @GrpcClientBean: 注解会把使用 @GrpcClient 注解的类型注册到 Spring Context 中,方便 @Autowired 和 @Qualifier 的使用。 这个注解可以重复添加到您的 @Configuration 类中。
  • Channel: Channel 是单个地址的连接池。 目标服务器可能是多个 gRPC 服务。 该地址将使用 NameResolver 做解析,最终它可能会指向一批固定数量或动态数量的服务端。
  • ManagedChannel: ManagedChannel 是 Channel 的一种特殊变体,因为它允许对连接池进行管理操作,例如将其关闭。
  • NameResolver、 NameResolver.Factory: 一个用于将地址解析为SocketAddress 列表的类 ,当与先前列出的服务端连表连接失败或通道空闲时,地址将会重新做解析。 参阅 Configuration -> Choosing the Target。
  • ClientInterceptor: 在每个 Channel 处理之前拦截它们。 可以用于日志、监测、元数据处理和请求/响应的重写。 grpc-spring-boot-starter 将自动接收所有带有 @GrpcGlobalClientInterceptor 注解以及手动注册在GlobalClientInterceptorRegistry 上的客户拦截器。 参阅 Configuration -> ClientInterceptor。
  • CallCredentials: 管理身份验证的组件。 它可以用于存储凭据和会话令牌。 它还可以用来身份验证,并且使用返回的令牌(例如 OAuth) 来授权实际请求。 除此之外,如果令牌过期并且重新发送请求,它可以续签令牌。 如果您的应用程序上下文中只存在一个 CallCredentials bean,那么 spring 将会自动将其附加到Stub(  Channel )。 CallCredentialsHelper工具类可以帮助您创建常用的 CallCredentials 类型和相关的StubTransformer
  • StubFactory: 一个用于从 Channel 创建特定 Stub 的工厂。 可以注册多个 StubFactory,以支持不同类型的 stub。 参阅 Configuration -> StubFactory。
  • StubTransformer: 所有客户端的 Stub 的注入之前应用的转换器。 参阅 Configuration -> StubTransformer。
pom.xml
/*
 * Copyright (c) 2016-2023 The gRPC-Spring Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.et.grpc.client;

import com.et.grpc.api.protobuf.lib.HelloReply;
import com.et.grpc.api.protobuf.lib.HelloRequest;
import com.et.grpc.api.protobuf.lib.MyServiceGrpc;
import org.springframework.stereotype.Service;

import io.grpc.StatusRuntimeException;
import net.devh.boot.grpc.client.inject.GrpcClient;


/**
 * @author Michael (yidongnan@gmail.com)
 * @since 2016/11/8
 */
@Service
public class GrpcClientService {

    @GrpcClient("local-grpc-server")
    // @GrpcClient("cloud-grpc-server")
    private MyServiceGrpc.MyServiceBlockingStub myServiceStub;

    public String sendMessage(final String name) {
        try {
            final HelloReply response = this.myServiceStub.sayHello(HelloRequest.newBuilder().setName(name).build());
            return response.getMessage();
        } catch (final StatusRuntimeException e) {
            return "FAILED with " + e.getStatus().getCode().name();
        }
    }

}
controller
/*
 * Copyright (c) 2016-2023 The gRPC-Spring Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.et.grpc.client;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Michael (yidongnan@gmail.com)
 * @since 2016/12/4
 */
@RestController
public class GrpcClientController {

    @Autowired
    private GrpcClientService grpcClientService;

    @RequestMapping("/")
    public String printMessage(@RequestParam(defaultValue = "Michael") String name) {
        return grpcClientService.sendMessage(name);
    }

}
application.yaml
server:
  port: 8088
spring:
  application:
    name: local-grpc-client

grpc:
  client:
    local-grpc-server:
      address: 'static://127.0.0.1:9898'
      enableKeepAlive: true
      keepAliveWithoutCalls: true
      negotiationType: plaintext

3.测试

  • 启动服务端
  • 启动客户端
  • 调用服务http://127.0.0.1:8088/

测试服务

您可以通过运行 gRPCurl 命令来测试您的应用程序是否正常运行:

grpcurl --plaintext localhost:9898 list
grpcurl --plaintext localhost:9898 list net.devh.boot.grpc.example.MyService
# Linux (Static content)
grpcurl --plaintext -d '{"name": "test"}' localhost:9898 net.devh.boot.grpc.example.MyService/sayHello
# Windows or Linux (dynamic content)
grpcurl --plaintext -d "{\"name\": \"test\"}" localhost:9898 net.devh.boot.grpc.example.MyService/sayHello

gRPCurl 的示例命令输出以及其他信息请参考此 文档 。

4.引用

  • 入门指南 | grpc-spring-boot-starter

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/787993.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spark SQL中的正则表达式应用

正则表达式是一种强大的文本处理工具,在Spark SQL中也得到了广泛支持。本文将介绍Spark SQL中使用正则表达式的主要方法和常见场景。 目录 1. 正则表达式函数1.1 regexp_extract1.2 regexp_replace1.3 regexp_like 2. 在WHERE子句中使用正则表达式3. 在GROUP BY中使用正则表达…

线程池案例

秒杀 需求 10个礼物20个客户抢随机10个客户获取礼物&#xff0c;另外10无法获取礼物 任务类 记得给共享资源加锁 public class MyTask implements Runnable{// 礼物列表private ArrayList<String> gifts ;// 用户名private String username;public MyTask( String user…

2024 WAIC|第四范式签约上海徐汇 加速推动“人工智能+千行百业”

7月5日&#xff0c;在“加速‘人工智能’构筑新质生产力”活动上&#xff0c;上海市徐汇区与作为大模型开发应用的核心企业第四范式举行签约仪式。徐汇区委常委、副区长俞林伟&#xff0c;第四范式联合创始人、总裁胡时伟等代表上台签约。 未来&#xff0c;双方将围绕人工智能前…

网络服务器配置与管理

网络服务器配置与管理是一个涉及多个方面的领域&#xff0c;它涵盖了从物理硬件的设置到操作系统、网络服务和应用的配置&#xff0c;再到日常维护和安全策略的实施。以下是网络服务器配置与管理的一些核心概念和步骤&#xff1a; 硬件配置&#xff1a; 选择合适的服务器硬件&a…

【多线程】wait()和notify()

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. 为什么需要wait()方法和notify()方法&#xff1f;2. wait()方法2.1 wait()方法的作用2.2 wait()做的事情2…

IDEA新建项目并撰写Java代码的方法

本文介绍在IntelliJ IDEA软件中&#xff0c;新建项目或打开已有项目&#xff0c;并撰写Java代码的具体方法&#xff1b;Groovy等语言的代码也可以基于这种方法来撰写。 在之前的文章IntelliJ IDEA社区版在Windows电脑中的下载、安装方法&#xff08;https://blog.csdn.net/zheb…

01 | 基础架构:一条SQL查询语句是如何执行的?

此系列文章为极客时间课程《MySQL 实战 45 讲》的学习笔记&#xff01; 引言 在了解 SQL 查询语句如何执行之前&#xff0c;先了解下MySQL 的基本架构示意图。 MySQL 分为 Server 层和引擎层。 Server 层包括连接器、查询缓存、分析器、优化器、执行器等&#xff0c;涵盖 M…

揭秘GPT-4o:未来智能的曙光

引言 近年来&#xff0c;人工智能&#xff08;AI&#xff09;的发展突飞猛进&#xff0c;尤其是自然语言处理&#xff08;NLP&#xff09;领域的进步&#xff0c;更是引人注目。在这一背景下&#xff0c;OpenAI发布的GPT系列模型成为了焦点。本文将详细探讨最新的模型GPT-4o&a…

【刷题汇总 -- 求最小公倍数、数组中的最长连续子序列、字母收集】

C日常刷题积累 今日刷题汇总 - day0081、求最小公倍数1.1、题目1.2、思路1.3、程序实现 -- 穷举法1.2、程序实现 -- 辗转相除法 2、数组中的最长连续子序列2.1、题目2.2、思路2.3、程序实现 3、字母收集3.1、题目3.2、思路3.3、程序实现 4、题目链接 今日刷题汇总 - day008 1、…

基于STM32的智能加湿器

1.简介 基于STM32的加湿器发展前景非常乐观&#xff0c;这主要得益于其在技术、市场需求、应用场景以及政策支持等多方面的优势。STM32微控制器具备强大的处理能力和丰富的外设接口&#xff0c;能够实现精确的湿度监测和智能化控制。基于STM32的加湿器可以根据环境湿度自动调节…

Spark实现电商消费者画像案例

作者/朱季谦 故事得从这一张图开始说起—— 可怜的打工人准备下班时&#xff0c;突然收到领导发来的一份电商消费者样本数据&#xff0c;数据内容是这样的—— 消费者姓名&#xff5c;年龄&#xff5c;性别&#xff5c;薪资&#xff5c;消费偏好&#xff5c;消费领域&#x…

使用各向异性滤波器和图像处理方法进行脑肿瘤检测(MATLAB)

医学图像分割一直以来都是计算机辅助诊断领域的研究热点。在医学图像的处理和分析中&#xff0c;对图像中感兴趣区域的准确分割尤其关键。要对感兴趣区域进行分类识别&#xff0c;首先要从图像中把感兴趣区域精确分割出来&#xff0c;然后有针对性地对感兴趣区域提取特征并分类…

使用clion刷leetcode

如何优雅的使用clion刷leetcode 安装插件&#xff1a;LeetCode Editor) 插件配置&#xff1a; 这样我们每打开一个项目&#xff0c;就会创建类似的文件 我们的项目结构&#xff1a; 我们在题解文件中导入头文件myHeader.h并将新建的文件添加到cmakelists.txt文件&#xff0c;…

初识CPlusPlus

前言 也是好久没写博客了&#xff0c;那些天也没闲着&#xff0c;去练题去了。实际上练题也可以写练题的博客&#xff0c;但是觉得太简单了些&#xff0c;于是就没有继续写下去。如今又回来写博客&#xff0c;是因为有整理了新的知识C。内容不算多&#xff0c;大多数都是书本上…

接口测试工具Apifox使用以及多环境的配置

下载 Apifox - API 文档、调试、Mock、测试一体化协作平台 - 接口文档工具&#xff0c;接口自动化测试工具&#xff0c;接口Mock工具&#xff0c;API文档工具&#xff0c;API Mock工具&#xff0c;API自动化测试工具 安装 正常安装 , 微信扫码注册 apifox中创建项目 安装idea插…

数学建模美赛入门

数学建模需要的学科知识 高等数学线性代数 有很多算法的掌握是需要高等数学和线代的相关知识 如&#xff1a;灰色预测模型需要微积分知识&#xff1b;神经网络需要用到导数知识&#xff1b;图论和层次分析法等都需要用到矩阵计算的相关知识等&#xff1b; 概率论与数理统计&am…

Xubuntu24.04之设置高性能模式两种方式(二百六十一)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+AOSP…

YOLOv8改进 在通道维度上引入注意力机制CPCAttention

一、CPCAttention论文 论文地址:2306.05196 (arxiv.org) 二、CPCAttention结构 Channel prior convolutional attention (CPCA)用于图像分类和目标检测任务中。CPCA能够在卷积神经网络中引入通道相关性,并通过自适应地学习到每个通道的权重,从而提升模型的性能。 CPCA的关…

纯前端如何实现Gif暂停、倍速播放

前言 GIF 我相信大家都不会陌生&#xff0c;由于它被广泛的支持&#xff0c;所以我们一般用它来做一些简单的动画效果。一般就是设计师弄好了之后&#xff0c;把文件发给我们。然后我们就直接这样使用&#xff1a; <img src"xxx.gif"/>这样就能播放一个 GIF …

通过rpmbuild构建Elasticsearch-7.14.2-search-guard的RPM包

系列文章目录 rpmbuild从入门到放弃 search-guard插件使用入门手册 文章目录 系列文章目录前言一、资源准备二、spec文件1.基础信息2.%prep3.%Install4.%file5.%post6.%postun 三、成果演示1.执行构建过程图示例2.执行安装RPM包示例3.进程检查4.访问esApi 总结 前言 不管是源…