当前位置: 首页 > news >正文

Spring Cloud系列—OpenFeign远程调用

上篇文章:

Spring Cloud系列—Nacos配置中心https://blog.csdn.net/sniper_fandc/article/details/149939799?fromshare=blogdetail&sharetype=blogdetail&sharerId=149939799&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link

目录

1 OpenFeign的使用

1.1 引入依赖

1.2 在启动类添加注解

1.3 编写客户端

1.4 远程调用代码

1.5 运行结果

2 OpenFeign参数传递

3 OpenFeign继承

3.1 公共接口的jar包

3.2 服务提供方实现公共接口ProductInterface

3.3 服务调用方继承公共接口ProductInterface

4 OpenFeign抽取

4.1 公共接口的jar包

4.2 服务调用方代码修改


        如果使用RestTemplate来进行远程调用,会发现拼接URL还是有点复杂,并且代码可读性差。

        Spring Cloud的OpenFeign(Feign的升级版)提供了更高级的远程服务调用方式。

1 OpenFeign的使用

        注意:本文的实现基于前面的文章。

1.1 引入依赖

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

        谁要远程调用别的服务,就给谁添加依赖。

1.2 在启动类添加注解

        在要进行远程调用的服务的启动类添加注解:

@EnableFeignClients

1.3 编写客户端

        该客户端类似于被远程调用的服务对应的方法的声明,在远程调用服务中编写该客户端,就可以让我们像对象的方法调用一样进行简洁的远程调用:

@FeignClient(value = "product-service", path = "/product")public interface ProductApi {@RequestMapping("/{productId}")ProductInfo getProductById(@PathVariable("productId") Integer productId);}

        @FeignClient(value = "product-service", path = "/product")注解声明该接口是远程调用product-service服务的接口,path表示该接口的所有方法的访问路径都具有统一前缀/product。

        @RequestMapping("/{productId}")注解定义了具体的方法实现路径(该实现路径是path+@RequestMapping注解内的值),即/product/{productId}(上述写法可以去掉path,@RequestMapping注解内填上完整的接口路径)。

        方法的声明格式就类似于product-service的方法实现一样,不一样的是productId是从路径中获取的,因此需要添加@PathVariable("productId")注解。

1.4 远程调用代码

@Servicepublic class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate ProductApi productApi;public OrderInfo selectOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectOrderById(orderId);//OpenFeign远程调用方式ProductInfo productInfo = productApi.getProductById(orderInfo.getProductId());orderInfo.setProductInfo(productInfo);return orderInfo;}}

1.5 运行结果

        运行程序,访问接口,结果如下:

2 OpenFeign参数传递

        OpenFeign同样也支持类似SpringMVC的请求中参数传递,以下分别演示单个参数、多个参数、对象、JSON的参数传递效果:

        首先需要在服务提供方创建相关的接口供服务调用方调用,这部分就是SpringMVC的相关知识:

@RequestMapping("/product")@RestControllerpublic class ProductController {//请求中传递单个参数@RequestMapping("/param1")public String param1(Integer productId){return "product-service收到请求中的参数为:Id:"+productId;}//请求中传递多个参数@RequestMapping("/param2")public String param2(Integer productId,String name){return "product-service收到请求中的参数为Id:"+productId+" name:"+name;}//请求中传递对象@RequestMapping("/object")public String object(ProductInfo productInfo){return "product-service收到请求中的参数为productInfo:"+productInfo.toString();}//请求中传递JSON(JSON格式是放在请求Body中传递的)@RequestMapping("/json")public String json(@RequestBody ProductInfo productInfo){return "product-service收到请求中的参数为productInfo的JSON格式:"+productInfo;}}

        然后在服务调用方定义OpenFeign的客户端,声明服务提供方的相关方法和url:

@FeignClient(value = "product-service", path = "/product")public interface ProductApi {//请求中传递单个参数(注意参数绑定)@RequestMapping("/param1")String param1(@RequestParam("productId") Integer productId);//请求中传递多个参数(注意参数绑定)@RequestMapping("/param2")String param2(@RequestParam("productId")Integer productId,@RequestParam("name")String name);//请求中传递对象(注意@SpringQueryMap是做对象绑定的)@RequestMapping("/object")String object(@SpringQueryMap ProductInfo productInfo);//请求中传递JSON(JSON格式是放在请求Body中传递的)@RequestMapping("/json")String json(@RequestBody ProductInfo productInfo);}

        最后服务调用方可以像调用对象的方法一样调用OpenFeign声明的方法,从而实现请求中传参的远程调用:

@RequestMapping("/order")@RestControllerpublic class OrderController {@Autowiredprivate ProductApi productApi;//order-service远程调用product-service的接口并传单个参数@RequestMapping("/feign-param1")public String feignParam1(@RequestParam("productId") Integer productId){return productApi.param1(productId);}//order-service远程调用product-service的接口并传多个参数@RequestMapping("/feign-param2")public String feignParam2(@RequestParam("productId") Integer productId, @RequestParam("name") String name){return productApi.param2(productId,name);}////order-service远程调用product-service的接口并传对象@RequestMapping("/feign-object")public String feignObject(ProductInfo productInfo){return productApi.object(productInfo);}////order-service远程调用product-service的接口并传JSON@RequestMapping("/feign-json")public String feignJson(@RequestBody ProductInfo productInfo){return productApi.json(productInfo);}}

        启动服务,各个方法的运行结果如下:

3 OpenFeign继承

        上述代码可以发现,在服务调用方实现OpenFeign客户端的代码和服务提供方实现的接口非常相似,那么我们就可以定义一个公共的接口,让服务提供方实现该接口,让服务调用方的OpenFeign客户端接口继承该接口,再把公共接口以一个单独公共的jar包导入项目中,实现项目的解耦。

3.1 公共接口的jar包

        该jar包内部结构如下:

        ProductInterface接口声明了远程调用的方法:

public interface ProductInterface {@RequestMapping("/{productId}")ProductInfo getProductById(@PathVariable("productId") Integer productId);//请求中传递单个参数(注意参数绑定)@RequestMapping("/param1")String param1(@RequestParam("productId") Integer productId);//请求中传递多个参数(注意参数绑定)@RequestMapping("/param2")String param2(@RequestParam("productId")Integer productId,@RequestParam("name")String name);//请求中传递对象(注意@SpringQueryMap是做对象绑定的)@RequestMapping("/object")String object(@SpringQueryMap ProductInfo productInfo);//请求中传递JSON(JSON格式是放在请求Body中传递的)@RequestMapping("/json")String json(@RequestBody ProductInfo productInfo);}

        ProductInfo类的内容就是order-service和product-service用到的公共产品信息类,路径建议和order-service和product-service中的ProductInfo类保持一致(当然不同项目中肯定无法100%一致),这样导入的jar后其它代码改动就较小。

        同时注意引入依赖,主要是SpringMVC和OpenFeign的依赖:

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

        最后就是通过Maven将该模块打成jar包导入本地Maven仓库:

3.2 服务提供方实现公共接口ProductInterface

        在服务提供方引入依赖,该依赖刚刚打包的依赖,可以直接先在要实现接口的类输入implements ProductInterface,然后用快捷键alt+Enter快捷引入pom文件中:

<dependency><groupId>org.example</groupId><artifactId>product_api</artifactId><version>1.0-SNAPSHOT</version><scope>compile</scope></dependency>

        修改服务提供方代码实现该接口:

@RequestMapping("/product")@RestControllerpublic class ProductController implements ProductInterface {@Autowiredprivate ProductService productService;@RequestMapping("/{productId}")public ProductInfo getProductById(@PathVariable("productId") Integer productId){System.out.println("收到请求,Id:"+productId);return productService.selectProductById(productId);}//请求中传递单个参数@RequestMapping("/param1")public String param1(Integer productId){return "product-service收到请求中的参数为:Id:"+productId;}//请求中传递多个参数@RequestMapping("/param2")public String param2(Integer productId,String name){return "product-service收到请求中的参数为Id:"+productId+" name:"+name;}//请求中传递对象@RequestMapping("/object")public String object(ProductInfo productInfo){return "product-service收到请求中的参数为productInfo:"+productInfo.toString();}//请求中传递JSON(JSON格式是放在请求Body中传递的)@RequestMapping("/json")public String json(@RequestBody ProductInfo productInfo){return "product-service收到请求中的参数为productInfo的JSON格式:"+productInfo;}}

3.3 服务调用方继承公共接口ProductInterface

        在服务调用方的OpenFeign客户端(接口)继承ProductInterface接口,也就继承了其中声明的方法:

@FeignClient(value = "product-service", path = "/product")public interface ProductApi extends ProductInterface {}

        注意:此时由于ProductInfo包也是通过jar引入,因此原服务中相关的包路径都需要修改。

        重启服务,测试远程调用接口均可以正常使用:

4 OpenFeign抽取

        通过继承的方式,实际上还是需要在服务调用方写OpenFeign客户端接口,服务调用方越多,越麻烦。OpenFeign还提供了抽取的方式,可以直接把整个OpenFeign客户端接口都抽取成公共的模块,其它服务调用方只需要注入该模块即可使用。

4.1 公共接口的jar包

        该jar包通常是由服务提供方制作,jar包的结构也类似继承方式时jar包的结构:

        不同之处在于公共接口中的内容比继承方式的接口中的内容多了OpenFeign的注解表明这是OpenFeign客户端:

@FeignClient(value = "product-service", path = "/product")public interface ProductInterface {@RequestMapping("/{productId}")ProductInfo getProductById(@PathVariable("productId") Integer productId);//请求中传递单个参数(注意参数绑定)@RequestMapping("/param1")String param1(@RequestParam("productId") Integer productId);//请求中传递多个参数(注意参数绑定)@RequestMapping("/param2")String param2(@RequestParam("productId")Integer productId,@RequestParam("name")String name);//请求中传递对象(注意@SpringQueryMap是做对象绑定的)@RequestMapping("/object")String object(@SpringQueryMap ProductInfo productInfo);//请求中传递JSON(JSON格式是放在请求Body中传递的)@RequestMapping("/json")String json(@RequestBody ProductInfo productInfo);}

        其它部分都同继承方式的内容,将该模块打包成jar包存入本地Maven仓库:

4.2 服务调用方代码修改

        同样也需要引入该jar包的依赖:

<dependency><groupId>org.example</groupId><artifactId>product_api2</artifactId><version>1.0-SNAPSHOT</version><scope>compile</scope></dependency>

        采用注入的方式来注入抽取的OpenFeign客户端接口(注意路径是新引入的jar包):

@RequestMapping("/order")@RestControllerpublic class OrderController {@Autowiredprivate OrderService orderService;@Autowiredprivate ProductInterface productInterface;@RequestMapping("/{orderId}")public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId) {return orderService.selectOrderById(orderId);}//order-service远程调用product-service的接口并传单个参数@RequestMapping("/feign-param1")public String feignParam1(@RequestParam("productId") Integer productId){return productInterface.param1(productId);}//order-service远程调用product-service的接口并传多个参数@RequestMapping("/feign-param2")public String feignParam2(@RequestParam("productId") Integer productId, @RequestParam("name") String name){return productInterface.param2(productId,name);}////order-service远程调用product-service的接口并传对象@RequestMapping("/feign-object")public String feignObject(ProductInfo productInfo){return productInterface.object(productInfo);}////order-service远程调用product-service的接口并传JSON@RequestMapping("/feign-json")public String feignJson(@RequestBody ProductInfo productInfo){return productInterface.json(productInfo);}}

        原先继承方式@FeignClient注解的OpenFeign客户端位于order-service的启动类同级目录下,因此该注解会在启动时被Spring扫描到从而作为Bean存储到Spring容器中。

        而抽取方式由于直接引入OpenFeign客户端的依赖,jar包不再启动类同级目录下,因此该Bean无法被扫描到,因此需要手动声明扫描该Bean的路径:

//方式1:扫描路径下所有的Bean,即用@FeignClient注释的类(更耗时)@EnableFeignClients(basePackages = {"com.demo.product.api2"})//方式2:指定OpenFeign客户端,只扫描@FeignClient注解注释的类//@EnableFeignClients(clients = {ProductInterface.class})@SpringBootApplicationpublic class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}}

        重启服务,测试远程调用接口:

下篇文章:

http://www.xdnf.cn/news/1276777.html

相关文章:

  • 【东枫科技】FR3 可扩展测试平台,适用于 6G 研究与卫星通信,高达 1.6 GHz 的带宽
  • 【Html网页模板】炫酷科技风公司首页
  • 正确使用SQL Server中的Hint(10)—Hint简介与Hint分类及语法(1)
  • strace的常用案例
  • GPT-5与中国AI发展(DeepSeek R1视角)
  • FFmpeg实现音视频转码
  • QT的常用控件说明
  • 【从源码角度深度理解 Python 的垃圾回收机制】:第1课引用计数篇
  • C++高频知识点(二十)
  • 电脑使用“碎片整理”程序的作用
  • Vue.js设计于实现 - 概览(二)
  • Vue 事件冒泡处理指南:从入门到精通
  • vue2升级vue3:单文件组件概述 及常用api
  • 基于VuePress2开发文档自部署及嵌入VUE项目
  • 【数据分析】循环移位岭回归分析:光遗传学冻结行为模式研究
  • 2025华数杯比赛还未完全结束!数模论文可以发表期刊会议
  • SAP学习笔记 - 开发57 - RAP开发 Managed App RAP action 之 Accept Travel 和 Reject Travel
  • special topic 8 (2) and topic 9 (1)
  • 一键复制产品信息到剪贴板
  • Kafka消费者相关原理
  • K8s DaemonSet 详解
  • es-drager-blog
  • 安全生产基础知识(一)
  • ThreadLocal的原理是什么,使用场景有哪些?
  • 状态机浅析
  • Linux操作系统从入门到实战(十八)在Linux里面怎么查看进程
  • Pico+unity VR入门开发超详细笔记2025
  • SpringBoot实现文件上传
  • 一些js数组去重的实现算法
  • 故障诊断 | VMD-CNN-BiLSTM西储大学轴承故障诊断附MATLAB代码