Spring中OpenFeign的使用方法最佳實(shí)踐
1. RestTemplate存在的問題
觀察我們之前遠(yuǎn)程調(diào)用的代碼
public OrderInfo selectOrder(Integer id){
OrderInfo orderInfo = orderMapper.selectOrderById(id);
//把獲取到的服務(wù)URL拼接到遠(yuǎn)程調(diào)用的URL中
String url = "http://product-service/product/select?id=" + orderInfo.getProductId();
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
雖然說RestTemplate對(duì)HTTP封裝之后,已經(jīng)比直接使用HTTP Client方便很多,但是還是存在一些問題的.
- 需要拼接URL,靈活性高,但是封裝臃腫,URL復(fù)雜的時(shí)候,容易出錯(cuò).
- 代碼可讀性差,風(fēng)格不統(tǒng)一.
微服務(wù)之間的通行主要有兩種方式,一種是RPC,一種是HTTP.在SpringCloud中,默認(rèn)是使用HTTP進(jìn)行通信的,最常用的實(shí)現(xiàn)形式有兩種:
- RestTemplate
- OpenFeign
接下來我們就來詳細(xì)介紹一下OpenFeign.
2. OpenFeign介紹
OpenFeign是一個(gè)聲明式的Web Service客戶端.它讓微服務(wù)之間的調(diào)用變得更加簡單,類似與Controller調(diào)用Service,只需要?jiǎng)?chuàng)建一個(gè)接口,然后再添加注解就可以使用OpenFeign.
2.1 引入依賴
首先我們需要在服務(wù)調(diào)用方(order-service)中引入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.2 添加注解
在order-service的啟動(dòng)類添加注解,@EnableFeignClients,開啟OpenFeign的功能.
@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class,args);
}
}
2.3 編寫OpenFeign客戶端
在調(diào)用其他服務(wù)的客戶端之下,編寫API調(diào)用接口,基于SpringMVC的注解來聲明遠(yuǎn)程調(diào)用的信息.

@FeignClient(name = "product-service")
public interface ProductApi {
@RequestMapping("/product/select")
ProductInfo getProductInfo(@RequestParam("id")Integer id);
}
[注意]:在接口的參數(shù)前面必須使用@RequestParam加上所調(diào)用服務(wù)接口的對(duì)應(yīng)參數(shù),否則參數(shù)無法傳入,就相當(dāng)于在請(qǐng)求的后面加上了?參數(shù)=xxx.

也可以把該服務(wù)的接口的統(tǒng)一前綴(放在類上的@RequestMapping)統(tǒng)一放到FeignClient注解的Path參數(shù)中.
@FeignClient(name = "product-service",path = "/product")
public interface ProductApi {
@RequestMapping("/select")
ProductInfo getProductInfo(@RequestParam("id")Integer id);
}
@FeignClient注解作用在接口上,參數(shù)說明:
- name/value: 指定OpenFeign的名稱,也就是被調(diào)用的微服務(wù)的名稱,用于服務(wù)發(fā)現(xiàn),Feign底層會(huì)使用SpringLoadBalance進(jìn)行負(fù)載均衡.
當(dāng)然也可以使用URL屬性指定一個(gè)具體的URL. - Path: 定義當(dāng)前FeignClient的統(tǒng)一前綴,一般是被調(diào)用方Controller層的類注解.
2.4 遠(yuǎn)程調(diào)用
修改遠(yuǎn)程調(diào)用方法
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private ProductApi api;
//設(shè)置原子類
public OrderInfo selectOrder(Integer id){
OrderInfo orderInfo = orderMapper.selectOrderById(id);
//把獲取到的服務(wù)URL拼接到遠(yuǎn)程調(diào)用的URL中
Integer productId = orderInfo.getProductId();
ProductInfo productInfo = api.getProductInfo(productId);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
2.5 測試
啟動(dòng)服務(wù).訪問接口,測試遠(yuǎn)程調(diào)用.

3. OpenFeign參數(shù)傳遞
3.1 傳遞單個(gè)參數(shù)
我們上面2.4的例子就是單個(gè)參數(shù)的調(diào)用.下面我們?cè)賮砼e一個(gè)例子
- 服務(wù)提供方product-service
@RestController
@RequestMapping("/product")
public class ProductController {
@RequestMapping("/p1")
public String p1(Integer id){
return "接收到參數(shù)"+id;
}
}
- FeginClient
@FeignClient(value = "product-service")
public interface ProductApi {
@RequestMapping("/product/p1")
String p1(@RequestParam("id") Integer id);
}
和我們上面提到的一樣,@RequestParam是用來做參數(shù)綁定的,不能省略.
3. order-service
@RequestMapping("/order")
@RestController
public class OrderController {
@Autowired
private ProductApi api;
@RequestMapping("/p1")
public String p1(Integer id){
return api.p1(id);
}
}
遠(yuǎn)程測試調(diào)用:GET http://127.0.0.1:8080/order/p1?id=1

3.2 傳遞多個(gè)參數(shù)
使用多個(gè)@RequestParam進(jìn)行參數(shù)綁定即可
- 服務(wù)提供方product-service
@RestController
@RequestMapping("/product")
public class ProductController {
@RequestMapping("/p2")
public String p2(Integer id1,Integer id2){
return "接收到參數(shù)1:" + id1 + ",接收到參數(shù)2:" + id2;
}
}
- FeignClient
@FeignClient(value = "product-service")
public interface ProductApi {
@RequestMapping("/product/p2")
String p2(@RequestParam("id1") Integer p1,@RequestParam("id2") Integer p2);
}
- 服務(wù)消費(fèi)方order-service
@RequestMapping("/order")
@RestController
public class OrderController {
@Autowired
private ProductApi api;
@RequestMapping("/p2")
public String p2(Integer p1,Integer p2){
return api.p2(p1,p2);
}
}
調(diào)用接口進(jìn)行測試:GET http://127.0.0.1:8080/order/p2?p1=1&p2=2

4.3 傳遞對(duì)象
- 服務(wù)提供方product-service
@RequestMapping("/p3")
public String p3(ProductInfo productInfo){
return "接收到對(duì)象:" + productInfo;
}
- Feign
@RequestMapping("/product/p3")
String p3(@SpringQueryMap ProductInfo productInfo);
其中@SpringQueryMap注解表示的是:可以方便地將一個(gè)對(duì)象的屬性作為請(qǐng)求的查詢參數(shù)添加到請(qǐng)求的 URL中,避免了手動(dòng)構(gòu)建查詢參數(shù)字符串(如json)的繁瑣過程.
3. order-service
@RequestMapping("/p3")
public String p3(ProductInfo productInfo){
return api.p3(productInfo);
}
測試遠(yuǎn)程調(diào)用:GET http://127.0.0.1:8080/order/p3?id=5&productName=zhangsan

3.4 傳遞Json
和我們之前學(xué)習(xí)SpringBoot的時(shí)候一樣,同樣是使用@RequestBody注解進(jìn)行綁定.
注意由于數(shù)據(jù)在服務(wù)之間一直是按照J(rèn)son格式傳遞的,所以服務(wù)方,服務(wù)調(diào)用方,FeignClient的參數(shù)上都需要加上注解.
- 服務(wù)提供方product-service
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo){
return "接收到對(duì)象:" + productInfo;
}
- FeignClient
@RequestMapping("/product/p4")
String p4(@RequestBody ProductInfo productInfo);
- 服務(wù)消費(fèi)方order-service
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo){
return api.p4(productInfo);
}
測試遠(yuǎn)程調(diào)用:http://127.0.0.1:8080/order/p4

4. 最佳實(shí)踐
通過觀察,我們也能看出來,Feign的客戶端與服務(wù)提供者的controller代碼非常相似.
Feign客戶端:
@FeignClient(value = "product-service")
public interface ProductApi {
@RequestMapping("/product/p1")
String p1(@RequestParam("id") Integer id);
}
服務(wù)提供方Controller
@RestController
@RequestMapping("/product")
public class ProductController {
@RequestMapping("/p1")
public String p1(Integer id){
return "接收到參數(shù)"+id;
}
}
有沒有一種方法可以簡化這種寫法呢?
4.1 Feign繼承方式
Feign支持繼承的方式,我們可以把一些常見的操作封裝到接口里.
我可以定義好一個(gè)接口,服務(wù)提供方實(shí)現(xiàn)這個(gè)接口,服務(wù)消費(fèi)方編寫Feign接口的時(shí)候,直接繼承這個(gè)接口.
接口可以放在一個(gè)公共的jar包中,供服務(wù)提供方和服務(wù)消費(fèi)方使用.
- 新建一個(gè)模塊

- 引入依賴
<dependencies>
<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>
</dependencies>
- 編寫接口
復(fù)制ProductInfo類和ProductApi到product-api模塊中.把ProductApi改名為ProductInterface
- 打jar包,并安裝到本地Maven倉庫

點(diǎn)擊install.該模塊就會(huì)被安裝到本地的Maven倉庫.
由于jar包中已經(jīng)有了ProductInfo類,所以我們就可以把product-service和order-service的ProductInfo類注掉了. - 服務(wù)提供方
需要先在服務(wù)方中引入對(duì)應(yīng)模塊的依賴.
<dependency>
<groupId>org.example</groupId>
<artifactId>product-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
服務(wù)方product-service直接實(shí)現(xiàn)接口ProductInterface.
@RestController
@RequestMapping("/product")
public class ProductController implements ProductInterface {
@Autowired
private ProductService productService;
@RequestMapping("/select")
public ProductInfo getProductInfo(Integer id){
return productService.selectById(id);
}
@RequestMapping("/p1")
public String p1(Integer id){
return "接收到參數(shù)"+id;
}
@RequestMapping("/p2")
public String p2(Integer id1,Integer id2){
return "接收到參數(shù)1:" + id1 + ",接收到參數(shù)2:" + id2;
}
@RequestMapping("/p3")
public String p3(ProductInfo productInfo){
return "接收到對(duì)象:" + productInfo;
}
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo){
return "接收到對(duì)象:" + productInfo;
}
}
- 服務(wù)消費(fèi)方
同樣,首先先添加依賴.
之后讓FeignClient繼承ProductInterface
@FeignClient(value = "product-service")
public interface ProductApi extends ProductInterface {
}
注意在上述步驟中,把ProductInfo類引入包的路徑全部改為之前打包的product-api模塊下ProductInfo類的路徑.
- 測試接口
http://127.0.0.1:8080/order/select?id=1
4.2 Feign抽取方式
官方推薦Feign的使用方式為繼承的方式,但是企業(yè)開發(fā)中,更多是把Feign接口抽取為?個(gè)獨(dú)立的模塊(做法和繼承相似,但理念不同).
操作方法:
將Feign的Client抽取為?個(gè)獨(dú)立的模塊,并把涉及到的實(shí)體類等都放在這個(gè)模塊中,打成?個(gè)Jar.服務(wù)消費(fèi)方只需要依賴該Jar包即可.這種方式在企業(yè)中比較常見,Jar包通常由服務(wù)提供方來實(shí)現(xiàn).
前4步和前面繼承的方式一樣.只不過接口ProductApi在復(fù)制到新模塊的時(shí)候不用修改名字

- 服務(wù)消費(fèi)方使用product-api
首先引入依賴,之后把該模塊中原有的ProductApi接口注掉.
<dependency> <groupId>org.example</groupId> <artifactId>product-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
之后指定掃描類
在啟動(dòng)類中添加需要加載的Feign客戶端.
@SpringBootApplication
@EnableFeignClients(clients = ProductApi.class)
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class,args);
}
}
- 測試
遠(yuǎn)程調(diào)用接口http://127.0.0.1:8080/order/select?id=1
總結(jié)
到此這篇關(guān)于Spring中OpenFeign的使用方法的文章就介紹到這了,更多相關(guān)Spring OpenFeign的使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JDK13.0.1安裝與環(huán)境變量的配置教程圖文詳解(Win10平臺(tái)為例)
這篇文章主要介紹了JDK13.0.1安裝與環(huán)境變量的配置教程圖文詳解(Win10平臺(tái)為例),本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01
使用@DS輕松解決動(dòng)態(tài)數(shù)據(jù)源的問題
這篇文章主要介紹了使用@DS輕松解決動(dòng)態(tài)數(shù)據(jù)源的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
java 讀取excel文件轉(zhuǎn)換成json格式的實(shí)例代碼
這篇文章主要介紹了 java 讀取excel文件轉(zhuǎn)換成json格式的實(shí)例代碼,需要的朋友可以參考下2018-04-04

