SpringCLoud OpenFeign的使用介绍

Spring Cloud OpenFeign使用介绍

导引

在之前的文章中,我们使用过RestTemplate来进行远程调用:

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private RestTemplate restTemplate;
    @Override
    public OrderInfo selectOrderById(Integer orderId) {
        OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
        String url = "http://product-service/product/" + orderInfo.getProductId();
        ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
        orderInfo.setProductInfo(productInfo);
        return orderInfo;
    }
}

虽说RestTemplate对HTTP封装后已经比直接使用HTTPClient简单方便很多,但还是存在这一些问题:

  • 需要拼接URL,灵活性高,但是封装臃肿,当URL复杂时则容易出错
  • 代码可读性差,风格不统一

于是,我们可以使用OpenFeign来更”优雅“的实现远程通信!

1. 简单介绍

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

OpenFeign 官方文档:GitHub - OpenFeign/feign: Feign makes writing java http clients easier

2. 操作方式

当前代码基于Nacos配置完成的基础上进行编写【代码获取】

  1. 先给order-service(哪个实例需要进行远程调用就给谁加)引入依赖

    <!--feign-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  2. order-service的启动类上添加注解@EnableFeignClients,开启OpenFeign功能

    package com.order;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @EnableFeignClients
    @SpringBootApplication
    public class OrderServiceApplication {
        public static void main(String[] args) {
            SpringApplication.run(OrderServiceApplication.class, args);
        }
    }
    
  3. 编写OpenFeign的客户端(接口),且只需进行方法声明,基于SpringMVC的注解来声明远程调用的信息

    package com.order.api;
    
    import com.order.model.ProductInfo;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.cloud.openfeign.SpringQueryMap;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import java.util.Date;
    
    @FeignClient(value = "product-service" , path = "/product")
    public interface ProductApi {
    
        @RequestMapping("/{productId}")
        ProductInfo getProductById(@PathVariable("productId") Integer productId);
    
    }
    

    • value:指定FeignClient的名称,也就是微服务的名称,用于服务发现
    • path:定义当前FeignClient的统一前缀
  4. 修改远程调用的方法

    package com.order.service.Impl;
    
    import com.order.api.ProductApi;
    import com.order.mapper.OrderMapper;
    import com.order.model.OrderInfo;
    import com.order.model.ProductInfo;
    import com.order.service.OrderService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.web.client.RestTemplate;
    
    @Service
    public class OrderServiceImpl implements OrderService {
    
        @Autowired
        private OrderMapper orderMapper;
    
        @Autowired
        private RestTemplate restTemplate;
    
        @Autowired
        private ProductApi productApi;
        @Override
        public OrderInfo selectOrderById(Integer orderId) {
            OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
    //        String url = "http://product-service/product/" + orderInfo.getProductId();
    //        ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
            ProductInfo productInfo = productApi.getProductById(orderInfo.getProductId());
            orderInfo.setProductInfo(productInfo);
            return orderInfo;
        }
    }
    

完成上述配置后,启动服务,访问接口测试远程调用:

在这里插入图片描述

远程调用测试成功,同时远程调用的代码也变的更加简洁了!

3. 参数传递

在上述例子中,介绍了从Feign从URL中获取参数,接下来介绍Feign参数传递的其它方式.

3.1 传递单个参数

服务提供方 product-service (ProductController)

package com.product.controller;

import com.product.service.model.ProductInfo;
import com.product.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

@Slf4j
@RequestMapping("/product")
@RestController
public class ProductController {

    @RequestMapping("p1")
    public String p1(Integer id) {
        return "p1接受单个参数Id = " + id;
    }
}

Feign客户端

package com.order.api;

import com.order.model.ProductInfo;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Date;

@FeignClient(value = "product-service" , path = "/product")
public interface ProductApi {
    
    @RequestMapping("/p1")
    String p1(@RequestParam("id") Integer id);
    
}

@RequestParam作参数绑定,不能省略

服务消费方 order-service (FeignController)

package com.order.controller;

import com.order.api.ProductApi;
import com.order.model.ProductInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RequestMapping("/feign")
@RestController
public class FeignController {

    @Autowired
    private ProductApi productApi;

    @RequestMapping("/o1")
    public String o1(Integer id) {
        log.info("调用o1");
        return productApi.p1(id);
    }
}

测试远程调用:http://127.0.0.1:8080/feign/o1?id=5

在这里插入图片描述

3.2 传递多个参数

使用多个@RequestParam进行参数绑定即可

服务提供方 product-service (ProductController)

@RequestMapping("p2")
public String p2(Integer id, String productName) {
    return "p2接收多个参数:id = " + id + " productName = " + productName;
}

Feign客户端

@RequestMapping("p2")
String p2(@RequestParam("id") Integer id, @RequestParam("productName") String productName);

服务消费方 order-service (FeignController)

@RequestMapping("/o2")
public String o2(Integer id, String productName) {
    log.info("调用o2");
    return productApi.p2(id, productName);
}

这里我们使用Postman进行远程调用测试:

在这里插入图片描述

3.3 传递对象

服务提供方 product-service (ProductController)

@RequestMapping("/p3")
public String p3(ProductInfo productInfo) {
    // 这里我们只给对象传入 id 和 productName,剩下的由内部封装如下
    productInfo.setProductPrice(99);
    productInfo.setState(0);
    productInfo.setCreateTime(new Date(System.currentTimeMillis()));
    productInfo.setUpdateTime(new Date(System.currentTimeMillis()));
    return productInfo.toString();
}

Feign客户端

@RequestMapping("/p3")
String p3(@SpringQueryMap ProductInfo productInfo);

:传入对象参数的话需要加上注解 @SpringQueryMap

服务消费方 order-service (FeignController)

@RequestMapping("/o3")
public String o3(ProductInfo productInfo) {
    log.info("调用o3");
    return productApi.p3(productInfo);
}

进行远程调用测试:

在这里插入图片描述

3.4 传递JSON类型参数

服务提供方 product-service (ProductController)

@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo) {
    productInfo.setProductPrice(99);
    productInfo.setState(0);
    productInfo.setCreateTime(new Date(System.currentTimeMillis()));
    productInfo.setUpdateTime(new Date(System.currentTimeMillis()));
    return productInfo.toString();
}

Feign客户端

@RequestMapping("/p4")
String p4(@RequestBody ProductInfo productInfo);

服务消费方 order-service (FeignController)

@RequestMapping("/o4")
    public String o4(@RequestBody ProductInfo productInfo) {
        log.info("调用o4");
        return productApi.p4(productInfo);
    }
}

进行远程调用测试:

在这里插入图片描述

4. 最佳实践

最佳实践,其实就是经过历史的迭代,在项目的实际过程中总结出来的最好的使用方式。

在上文配置Feign客户端时也能看出,它与服务提供方的代码非常相似:

在这里插入图片描述

如果需要在每个模块中都配置重复的代码,一旦数量上来则会造成一定的代码冗余!

对此我们可以对其做出简化,将Feign的客户端抽取为一个独立的模块,并把涉及到的实体类等都放在这个模块中,打成一个jar包,消费方只需要依赖该jar包即可(jar包通常由服务提供方来实现)

具体操作如下:

  1. 先创建一个模块 product-api

    在这里插入图片描述

  2. 给刚创建的模块product-api引入依赖

    <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>
    
  3. 复制ProductApiProductInfoproduct-api模块中

    在这里插入图片描述

  4. 通过Maven将该模块打包到本地Maven仓库

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

  5. 删除 order-service 中的ProductApi与ProductInfo

  6. 为服务消费方 order-service引入依赖

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

    会出现报错信息,此时需要修改项目中ProductApi与ProductInfod 路径product-api中的路径

  7. 给服务消费方 order-service启动类添加扫描路径

    @EnableFeignClients(basePackages = {“com.api”}, clients = {ProductApi.class})

    其中basePackages为扫描路径,clients为需要加载的Feign客户端

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

完成上述配置后即可进行远程测试调用:

在这里插入图片描述

:若需要将服务部署到服务器上,需要修改服务消费方的pom文件,因为其使用了部署在本地Maven仓库的jar包:

<dependency>
     <groupId>org.example</groupId>
     <artifactId>product-api</artifactId>
     <version>1.0-SNAPSHOT</version>
<!--  <scope>compile</scope>-->
     <scope>system</scope>
     <systemPath>D:/maven_test/.m2/repository/org/example/product-api/1.0-SNAPSHOT/product-api-1.0-SNAPSHOT.jar</systemPath>
</dependency>

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

以上便是对OpenFeign的使用介绍了!!如果内容对大家有帮助的话请给这篇文章一个三连关注吧💕( •̀ ω •́ )✧( •̀ ω •́ )✧✨

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值