SpringCloud  OpenFeign的使用举例详解

 更新时间:2025年09月18日 15:33:26   作者:程序猿教你打篮球  
文章介绍Spring Cloud中使用OpenFeign替代RestTemplate进行微服务通信,通过声明式接口和注解简化调用,建议将Feign客户端抽离为独立模块,统一实体类,解决代码冗余问题,部署时需配置本地jar包依赖,确保服务调用正常,感兴趣的朋友跟随小编一起看看吧

1. OpenFeign

1.1 OpenFeign 的介绍

先来看看咱们之前写的远程方法调用代码:

@RequestMapping("/ok")
public String ok(@PathParam("content")String content) {
    String url = "http://waiter-service/waiter/up/{content}";
    String resp = restTemplate.getForObject(url, String.class, content);
    return "调用成功, 已收到 waiter 的响应: " + resp;
}

虽然说 RestTemplate 对 http 封装后,使用起来还算方便,但是需要拼接 url,如果 url 很复杂呢?而且代码可读性很差,风格也不好统一。

在微服务之间的通信方式通常分为:RPC 和 HTTP,至于 RPC 后期有机会在介绍。

在 SpringCloud 中,默认使用的是 HTTP 来进行微服务的通信,最常用的实现有两种:

  • RestTemplate
  • OpenFeign

RestTemplate 咱们已经见过了,接下来就学习下 OpenFeign,这是一种更优雅的远程方法调用的形式。

OpenFeign 是一个声明式的 Web Service 客户端,它让微服务之间的调用变得更简单了。类似于 Controller 调用 Service,只需要创建一个接口,然后添加注解即可以使用 OpenFeign。

本来是 Feign 是由 Netflix 公司开源的组件,后来呢,在16年7月发布了最后一个版本,就将捐给了社区,16年7月,OpenFeign 首个版本 9.0.0 发布后,就一直发布到现在。

SpringCloud 将 Feign 项目继承到 SpringCloud 的生态中,但是受到 Feign 更名的影响,所以 SpringCloudFeign 有两个 starter。

spring-cloud-starter-feign spring-cloud-starter-openfeign

由于 Feign 停止维护,咱们的项目中使用的是后者 OpenFeign。

1.2 快速使用 OpenFeign

在 cook-service 引入 OpenFeign 依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

cook-service 启动类添加注解:@EnableFeignClients 开启 OpenFeign 功能。

package com.zlcode.cook;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class CookServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(CookServiceApplication.class, args);
    }
}

编写 OpenFeign 客户端,基于 SpringMVC 注解来声明远程调用的信息。

在 cook 目录下创建 api 目录,在这个 api 目录中创建 WaiterApi 接口。

package com.zlcode.cook.api;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(value = "waiter-service", path = "/waiter")
public interface WaiterApi {
    @RequestMapping("/up/{content}")
    String up(@PathVariable("content") String content);
}

上述 @FeignClient 注解中的 value 表示注册的服务名,用于服务发现,Feign底层会使用 Spring Cloud Load Balance 进行负载均衡,如果使用了 Nacos 负载均衡策略则使用的是 Nacos 的负载均衡。path 表示统一接口前缀,与咱们的 WaiterController 对应的。

WaiterController

package com.zlcode.waiter.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/waiter")
@Slf4j
public class WaiterController {
    @RequestMapping("/up/{content}")
    public String up(@PathVariable("content") String content) {
        log.info("正在执行: " + content);
        return "执行: " + content + "成功!";
    }
}

修改 CookController 的远程方法调用代码:

package com.zlcode.cook.controller;
import com.zlcode.cook.api.WaiterApi;
import jakarta.websocket.server.PathParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/cook")
public class CookController {
    @Autowired
    private WaiterApi waiterApi;
    @RequestMapping("/ok")
    public String ok(@PathParam("content")String content) {
        String resp = waiterApi.up(content);
        return "调用成功, 已收到 waiter 的响应: " + resp;
    }
}

重启 cook-service,再去浏览器中访问:http://127.0.0.1:8080/cook/up?content=给25桌上红烧肉

1.3 接口返回的是自定义对象该怎么办?

咱们例子中,waiter-service 的 waiter/up/ 接口返回值是 String,每个 Java 项目都可以用 String 这个对象,如果新增一个接口,获取服务员信息呢?返回的是一个 WaiterInfo 对象,那么在 cook-service 调用方该如何接收呢?

其实方法很简单,咱们先在 WatierController 新增一个 get-info 接口:

package com.zlcode.waiter.controller;
import com.zlcode.waiter.model.WaiterInfo;
import jakarta.websocket.server.PathParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/waiter")
@Slf4j
public class WaiterController {
    @RequestMapping("/up/{content}")
    public String up(@PathVariable("content") String content) {
        log.info("正在执行: " + content);
        return "执行: " + content + "成功!";
    }
    @RequestMapping("/get-info")
    public WaiterInfo getInfo(String name) {
        WaiterInfo waiterInfo = new WaiterInfo();
        waiterInfo.setWaiterId(1111);
        waiterInfo.setWaiterName(name);
        return waiterInfo;
    }
}

在 cook-service 的 WatierApi 接口中新增远程调用 waiter-service 提供的 get-info 的接口:

此时发现咱们的 cook-service 项目中没有 WaiterInfo 这个类,这也好办,去 waiter-service 中把 WaiterInfo 类复制到 cook-service 的 model 目录中。

package com.zlcode.cook.api;
import com.zlcode.cook.model.WaiterInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "waiter-service", path = "/waiter")
public interface WaiterApi {
    @RequestMapping("/up/{content}")
    String up(@PathVariable("content") String content);
    @RequestMapping("/get-info")
    WaiterInfo getInfo(@RequestParam("name") String name);
}

果然解决了这个问题,再来到 CookController 中新增接口去远程调用 get-info 方法:

package com.zlcode.cook.controller;
import com.zlcode.cook.api.WaiterApi;
import com.zlcode.cook.model.WaiterInfo;
import jakarta.websocket.server.PathParam;
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;
@RestController
@RequestMapping("/cook")
public class CookController {
    @Autowired
    private WaiterApi waiterApi;
    @RequestMapping("/ok")
    public String ok(@PathParam("content")String content) {
        String resp = waiterApi.up(content);
        return "调用成功, 已收到 waiter 的响应: " + resp;
    }
    @RequestMapping("/get")
    public String get(@RequestParam("name")String name) {
        WaiterInfo waiterInfo = waiterApi.getInfo(name);
        return "调用成功, 已收到 waiter 的响应: " + waiterInfo;
    }
}

重启 cook-service 和 waiter-service,在浏览器中访问:http://127.0.0.1:8080/cook/get?name=张三

调用成功,拿到了 waiter-service get-info 接口返回的 WaiterInfo 对象。

但是咱们仔细想想这段代码,在 cook-service 项目中存在 WaiterInfo 实体类,在 waiter-service 项目中也存在 WaiterInfo 实体类。那随着业务的增多,这样的冗余代码也会越来越多。而且咱们的 WaiterAPI 和 WaiterController 的代码也十分相似。

所以上述这样的写法是不行的,不是最佳的写法,更好的办法是把把 OpenFeign 抽离出来,作为一个独立的模块,服务方把提供的 API 都封装到这个独立的模块中,供消费方使用。

1.4 OpenFeign 的最佳实践

官方推荐的方式是继承,但是企业中用的不多,咱们此处只介绍抽离的方式,就像 6.3 最后说的,把 OpenFeign 提取出来成为一个独立的模块即可,这个模块由服务方去提供。

简单来说,将 OpenFeign 的 Client 抽取为⼀个独⽴的模块,并把涉及到的实体类等都放在这个模块中,打成⼀个 jar。消费方只需要依赖该 jar 包即可。

步骤如下:

创建一个 Model

引入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-loadbalancer</artifactId>
    </dependency>
</dependencies>

编写 API

将 cook-service 的 WaiterApi 和 WatierInfo 直接复制进来:

将这个 waiter-service-api 打包成 jar 包,单击 maven 选项中的 install:

将 cook-service 的 WaiterApi 和 WatierInfo 删除掉,然后引入咱们自己打包的 waiter-service-api 依赖

<dependency>
    <groupId>org.example</groupId>
    <artifactId>waiter-service-api</artifactId>
    <version>1.0-SNAPSHOT</version>
    <scope>compile</scope>
</dependency>

在启动类添加扫描路径:

package com.zlcode.cook;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients(basePackages = "com.zlcode.api")
public class CookServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(CookServiceApplication.class, args);
    }
}

也可以指定需要加载的 Feign客户端:

@EnableFeignClients(basePackageClasses = WaiterApi.class)
@EnableFeignClients(basePackages = "com.zlcode.api")

接着重新启动 cook-service 项目,然后在浏览器中访问:http://127.0.0.1:8080/cook/get?name=张三

这种更优雅的远程方法调用也就成功了!

1.5 部署时需要注意事项

由于当前项目中使用到了自己封装的 jar 包,但是 maven 打包默认会从中央仓库中去获取,但是咱们的 waiter-service-api 是在本地,这就比较麻烦,目前有三种解决方案:

  • 将 waiter-service-api jar 包上传到 maven 中央仓库,但需要注册申请,比较麻烦。
  • 搭建 maven 私服,上传 jar 包到私服,也是较麻烦的,企业中比较推荐。
  • 从本地读取 jar 包,这是在个人项目阶段中比较推荐的。只需要在引入本地 jar 包的项目中修改 pom.xml 文件就 ok 了。

观先获取 waiter-service-api 执行 install 后的本地 jar 包路径(通过控制台查看):

把之前引入的依赖替换成本地目录(记得将 \ 换成 /):

<dependency>
    <groupId>org.example</groupId>
    <artifactId>waiter-service-api</artifactId>
    <version>1.0-SNAPSHOT</version>
    <scope>system</scope>
    <systemPath>
        C:/xxx/xxx/.m2/repository/org/example/waiter-service-api/1.0-SNAPSHOT/waiter-service-api-1.0-SNAPSHOT.jar
    </systemPath>
</dependency>

把 build 配置项换成:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <includeSystemScope>true</includeSystemScope>
            </configuration>
        </plugin>
    </plugins>
</build>

includeSystemScope 允许包括系统本地的 jar 包。

接下来就可以进行快乐的部署到 Linux 服务器啦,剩下部署的操作这里就不再赘述了。

到此这篇关于SpringCloud OpenFeign的使用的文章就介绍到这了,更多相关SpringCloud OpenFeign使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java基础开发之JDBC操作数据库增删改查,分页查询实例详解

    Java基础开发之JDBC操作数据库增删改查,分页查询实例详解

    这篇文章主要介绍了Java基础开发之JDBC操作数据库增删改查,分页查询实例详解,需要的朋友可以参考下
    2020-02-02
  • springboot项目mysql-connector-java默认版本如何查看

    springboot项目mysql-connector-java默认版本如何查看

    这篇文章主要介绍了springboot项目mysql-connector-java默认版本如何查看问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 基于springboot的flowable工作流实战流程分析

    基于springboot的flowable工作流实战流程分析

    这篇文章主要介绍了基于springboot的flowable工作流实战流程分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-10-10
  • java使用elasticsearch分组进行聚合查询过程解析

    java使用elasticsearch分组进行聚合查询过程解析

    这篇文章主要介绍了java使用elasticsearch分组进行聚合查询过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • spring cloud 使用Zuul 实现API网关服务问题

    spring cloud 使用Zuul 实现API网关服务问题

    这篇文章主要介绍了spring cloud 使用Zuul 实现API网关服务问题,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-05-05
  • Springboot整合Netty实现RPC服务器的示例代码

    Springboot整合Netty实现RPC服务器的示例代码

    这篇文章主要介绍了Springboot整合Netty实现RPC服务器的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • Java16新特性record类使用细节示例详解

    Java16新特性record类使用细节示例详解

    这篇文章主要为大家介绍了Java16新特性record类使用细节示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • sharding-jdbc5.0.0实现分表实践

    sharding-jdbc5.0.0实现分表实践

    本文主要介绍了sharding-jdbc5.0.0分表实践,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • SpringBoot加载profile全面解析

    SpringBoot加载profile全面解析

    SpringBoot的Profile机制通过多配置文件和注解实现环境隔离,支持开发、测试、生产等不同环境的灵活配置切换,无需修改代码,关键点包括配置文件命名规范、激活方式、优先级及企业级安全部署实践,本文介绍SpringBoot加载profile的过程,感兴趣的朋友一起看看吧
    2025-08-08
  • Java如何获取Cookie和Session

    Java如何获取Cookie和Session

    Cookie 和 Session之间主要是通过 SessionId 关联起来的, SessionId是 Cookie 和 Session 之间的桥梁,这篇文章主要介绍了Java获取Cookie和Session的方法,需要的朋友可以参考下
    2024-01-01

最新评论