Spring Cloud OpenFeign使用介绍
文章目录
- Spring Cloud OpenFeign使用介绍
- 导引
- 1. 简单介绍
- 2. 操作方式
- 3. 参数传递
- 3.1 传递单个参数
- 3.2 传递多个参数
- 3.3 传递对象
- 3.4 传递JSON类型参数
- 4. 最佳实践
导引
在之前的文章中,我们使用过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配置完成的基础上进行编写【代码获取】:
-
先给
order-service
(哪个实例需要进行远程调用就给谁加)引入依赖<!--feign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
在
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); } }
-
编写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的统一前缀
-
修改远程调用的方法
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包通常由服务提供方来实现)
具体操作如下:
-
先创建一个模块
product-api
-
给刚创建的模块
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>
-
复制ProductApi、ProductInfo 到
product-api
模块中 -
通过Maven将该模块打包到本地Maven仓库
-
删除
order-service
中的ProductApi与ProductInfo -
为服务消费方
order-service
引入依赖<dependency> <groupId>org.example</groupId> <artifactId>product-api</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency>
会出现报错信息,此时需要修改项目中ProductApi与ProductInfod 路径为
product-api
中的路径 -
给服务消费方
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的使用介绍了!!如果内容对大家有帮助的话请给这篇文章一个三连关注吧💕( •̀ ω •́ )✧( •̀ ω •́ )✧✨