SpringCloud學習筆記(3):使用Feign實現宣告式服務呼叫
簡介
Feign是一個宣告式的Web Service客戶端,它簡化了Web服務客戶端的編寫操作,相對於Ribbon+RestTemplate的方式,開發者只需通過簡單的介面和註解來呼叫HTTP API。它支援Spring MVC註解和JAX-RS註解,還支援可插拔式的編碼器和解碼器。整合了Eureka,Ribbon和Hystrix,具有可插拔、基於註解、負載均衡、服務熔斷等一系列便捷功能。
專案介紹
- sc-parent,父模組(請參照SpringCloud學習筆記(1):Eureka註冊中心)
- sc-eureka,註冊中心(請參照SpringCloud學習筆記(1):Eureka註冊中心)
- sc-provider,提供者(請參照SpringCloud學習筆記(1):Eureka註冊中心)
- sc-consumer-feign,基於Feign宣告式呼叫的消費者
基於Feign宣告式呼叫的消費者
1.在父模組下建立子模組專案sc-consumer-feign,pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.cf</groupId> <artifactId>sc-parent</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>sc-consumer-feign</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> </project>
2.建立啟動類feign.FeignApplication:
package feign; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class FeignApplication { public static void main(String[] args) { SpringApplication.run(FeignApplication.class, args); } }
3.建立Feign宣告式介面:feign.inter.BookService
package feign.inter;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient("sc-provider")
public interface BookService {
@GetMapping("/book/list")
public String getBookList();
}
4.建立呼叫提供者服務的Controller:
package provider.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/book")
@RestController
public class BookController {
@GetMapping("/list")
public String getBookList(){
return "[\"Java入門到放棄\",\"C++入門到放棄\",\"Python入門到放棄\",\"C入門到放棄\"]";
}
}
5.建立application.yml:
server:
port: 8084
spring:
application:
name: sc-consumer-feign
eureka:
client:
registerWithEureka: false
serviceUrl:
defaultZone: http://localhost:8080/eureka/
6.依次啟動註冊中心sc-eureka、提供者sc-provider、消費者sc-consumer-feign,並訪問http://localhost:8084/feign/getBookList:
Feign基於Ribbon實現,也具有Ribbon負載均衡的特性,可以將呼叫的提供者服務換成sc-provider-random(請參照SpringCloud學習筆記(2):使用Ribbon負載均衡)來測試。
帶引數的請求
上面例子沒有涉及到引數的傳遞,接下來測試下如何使用Feign構造帶引數的請求,首先對提供者和消費者做如下更改:
//提供者Controller添加了兩個引數,並列印到控制檯。
@RequestMapping("/book")
@RestController
public class BookController {
@GetMapping("/list")
public String getBookList(String param1, Integer param2){
System.out.println(param1 + ":" + param2);
return "[\"Java入門到放棄\",\"C++入門到放棄\",\"Python入門到放棄\",\"C入門到放棄\"]";
}
}
//消費者Feign介面和Controller新增引數
@FeignClient("sc-provider")
public interface BookService {
@GetMapping("/book/list")
public String getBookList(String param1, Integer param2);
}
@RequestMapping("/feign")
@RestController
public class FeignController {
@Autowired
private BookService bookService;
@GetMapping("/getBookList")
public String getBookList(){
return bookService.getBookList("Java", 520);
}
}
依次啟動註冊中心sc-eureka、提供者sc-provider、消費者sc-consumer-feign,啟動消費者sc-consumer-feign時會啟動失敗:
java.lang.IllegalStateException: Method has too many Body parameters: public abstract java.lang.String feign.inter.BookService.getBookList(java.lang.String,java.lang.Integer)
解決方法1
更改Feign介面,為引數新增@RequestParam註解:
@FeignClient("sc-provider")
public interface BookService {
@GetMapping("/book/list")
public String getBookList(@RequestParam("param1") String param1, @RequestParam("param2") Integer param2);
}
解決方法2
將引數封裝到Map裡,更改消費者Feign介面和Controller:
@FeignClient("sc-provider")
public interface BookService {
@GetMapping("/book/list")
public String getBookList(@RequestParam Map<String, Object> paramMap);
}
@RequestMapping("/feign")
@RestController
public class FeignController {
@Autowired
private BookService bookService;
@GetMapping("/getBookList")
public String getBookList(){
Map<String,Object> paramMap = new HashMap<String, Object>();
paramMap.put("param1", "Java");
paramMap.put("param2", 520);
return bookService.getBookList(paramMap);
}
}
在引數較多的情況下,該方式可以簡化Feign介面的編寫。
自定義型別的引數
OpenFeign的@QueryMap註解支援將自定義型別用於GET引數對映,由於@QueryMap和Spring不相容,Spring Cloud OpenFeign提供了一個等價的@SpringQueryMap註解,可以用於自定義型別和Map型別的引數對映。下面將使用自定義型別Params作為引數,使用@SpringQueryMap註解來處理自定義型別的引數對映。
1.分別在提供者和消費者中建立類Params(可以建一個公共模組,然後在提供者和消費者中新增依賴):
public class Params {
private String param1;
private Integer param2;
public String getParam1() {
return param1;
}
public void setParam1(String param1) {
this.param1 = param1;
}
public Integer getParam2() {
return param2;
}
public void setParam2(Integer param2) {
this.param2 = param2;
}
@Override
public String toString() {
return "Params [param1=" + param1 + ", param2=" + param2 + "]";
}
public Params(String param1, Integer param2) {
this.param1 = param1;
this.param2 = param2;
}
public Params() {}
}
2.更改提供者和消費者相關類
//提供者
@RequestMapping("/book")
@RestController
public class BookController {
@GetMapping("/list")
public String getBookList(Params params){
System.out.println(params.toString());
return "[\"Java入門到放棄\",\"C++入門到放棄\",\"Python入門到放棄\",\"C入門到放棄\"]";
}
}
//消費者
@FeignClient("sc-provider")
public interface BookService {
@GetMapping("/book/list")
public String getBookList(@SpringQueryMap Params params);
}
@RequestMapping("/feign")
@RestController
public class FeignController {
@Autowired
private BookService bookService;
@GetMapping("/getBookList")
public String getBookList(){
Params params = new Params("Java", 520);
return bookService.getBookList(params);
}
}
3.依次啟動註冊中心sc-eureka、提供者sc-provider、消費者sc-consumer-feign,並訪問http://localhost:8084/feign/getBookList,提供者控制檯輸出:
Params [param1=Java, param2=520]