Spring Cloud OpenFeignで遊ぶ

先日のSpring Oneで、Unleash the True Power of Spring Cloud: Learn How to Customize Spring Cloudで紹介されていたSpring Cloud OpenFeignで遊んでみました。 素敵すぎて、もっと早く知りたかった...と感じました。

Spring Cloud OpenFeignとは

2018年6月にリリースされたSpring BootのためのOpenFeignインテグレーションです。 Spring Cloud OpenFeignを使うと宣言的にRESTクライアントを作成することができます。

サンプルコード

サンプルで使用しているライブラリ等は次の通りです。

Java 11
Maven
Spring Boot 2.3.3.RELEASE
Spring MVC
Spring Cloud OpenFeign 2.2.5.RELEASE

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  
  ...

    <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>
            <version>2.2.5.RELEASE</version>
        </dependency>
    </dependencies>
  
  ...

</project>

spring-cloud-starter-openfeignを依存関係に追加してください。

FeignConfig.java

@Configuration(proxyBeanMethods = false)
@EnableFeignClients(basePackages = {"com.b1a9idps.consumer.client"})
public class FeignConfig {}

@EnableFeignClients を付与することでデフォルト設定を使うことができるようになります。

SakeClient.java

@FeignClient(value = "sakes", url = "${producer.url}")
public interface SakeClient {
    @GetMapping(value = "/sakes")
    List<SakeResponse> list();

    @GetMapping(value = "/sakes/{id}")
    SakeResponse get(@PathVariable Integer id);

    @PostMapping(value = "/sakes")
    SakeResponse create(SakeCreateRequest request);
}

クライアントインターフェースを用意します。コントローラを実装する感覚でインターフェースを書くことができます。 インターフェースに @FeignClient を付与して、コンポーネントスキャンの対象にします。

@FeignClientのプロパティ
- url:リクエスト先のURL。
- name(value):任意のクライアント名。RibbonロードバランサーやSpring Cloud LoadBalancerを利用する際に使われる。

SakeController.java

@RestController
@RequestMapping("/sakes")
public class SakeController {

    final SakeClient sakeClient;

    public SakeController(SakeClient sakeClient) {
        this.sakeClient = sakeClient;
    }

    @GetMapping
    public List<SakeResponse> list() {
        return sakeClient.list();
    }

    @GetMapping("{id}")
    public SakeResponse get(@PathVariable Integer id) {
        return sakeClient.get(id);
    }

    @PostMapping
    public SakeResponse create() {
        return sakeClient.create(new SakeCreateRequest("寫樂", "宮泉銘醸"));
    }
}

クライアントをDIしてメソッドコールするだけです。

実行結果

アプリケーションを起動してcurlAPIを叩いた結果です。

$ curl -v http://localhost:8080/api/sakes | jq .
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /api/sakes HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Fri, 04 Sep 2020 04:12:38 GMT
< 
{ [172 bytes data]
100   166    0   166    0     0  14628      0 --:--:-- --:--:-- --:--:-- 15090
* Connection #0 to host localhost left intact
[
  {
    "id": 1,
    "name": "若波",
    "brewingName": "若波酒造"
  },
  {
    "id": 2,
    "name": "新政",
    "brewingName": "新政酒造"
  },
  {
    "id": 3,
    "name": "十四代",
    "brewingName": "高木酒造"
  }
]

補足

リクエストログをみたい

logging.level.[clientが置いてあるパッケージ]=debug
feign.client.config.default.logger-level=basic