---
title: TypeSafeで使いやすいJavaのハイレベルRESTクライアントRetrofit
tags: ["Java", "Retrofit"]
categories: ["Programming", "Java", "retrofit"]
date: 2014-05-05T06:49:28Z
updated: 2014-05-05T15:12:21Z
---

JavaのRESTクライアントといれば
* Spring FrameworkのRestClient
* JAX-RS 2のClient API
あたりがメジャーで使いやすい。

ただし、ちょっとboilerplateなコードが増えてしまう。[この記事][1]のTodoRestRepositoryとか。

最近見つけた[Retrofit][2]がよさげ。Interfaceだけ作れば実装はライブラリが提供してくれる(Proxyを作成してくれる)のが今風な作り。

以下、使用例

### pom.xml

```xml
<dependency>
    <groupId>com.squareup.retrofit</groupId>
    <artifactId>retrofit</artifactId>
    <version>1.5.0</version>
</dependency>
```

### REST Client

インタフェースのメソッドに対して、対応するHTTPのアノテーションをつけるだけ。

```java
package restclient;

import retrofit.http.GET;
import retrofit.http.Path;
import retrofit.http.Query;


public interface EntryRestRepository {
    @GET("/entries")
    Page<Entry> findAll();

    @GET("/entries")
    Page<Entry> findAll(@Query("page") int page, @Query("size") int size);

    @GET("/entries/{entryId}")
    Entry findOne(@Path("entryId") Long entryId);
}
```

非同期にも対応している。

入出力に対応するモデルは普通のJavaBeanでOK。

```java
package restclient;

@lombok.Data
public class Entry {
    private Long entryId;
    private String title;
    private String contents;
    private String format;
}
```

ネストにも対応している。

```java
package restclient;

import java.util.List;

@lombok.Data
public class Page<T> {
    private List<T> content;
    private String sort;
    private long totalPages;
    private long totalElements;
    private boolean firstPage;
    private boolean lastPage;
    private long numberOfElements;
    private int size;
    private int number;
}
```

### 使用例

```java
public static void main(String[] args) {
    RestAdapter restAdapter = new RestAdapter.Builder()
            .setEndpoint("http://blog.ik.am/api/v1")
            .build();
    EntryRestRepository repository = restAdapter.create(EntryRestRepository.class);

    System.out.println(repository.findAll());
    System.out.println(repository.findAll(0, 2));
    System.out.println(repository.findOne(259L));
}
```

### 非同期処理

返り値をvoidにして引数に`retrofit.Callback`を取ればよい。

```java
@GET("/entries")
void findAllAsynnc(@Query("page") int page, @Query("size") int size, Callback<Page> cb);

@GET("/entries/{entryId}")
void findOneAsync(@Path("entryId") Long entryId, Callback<Entry> cb);
```

使い方は、↓な感じ。

```java
repository.findOneAsync(260L, new Callback<Entry>() {
    @Override
    public void success(Entry entry, Response response) {
        System.out.println(entry);
    }

    @Override
    public void failure(RetrofitError error) {
        error.printStackTrace();
    }
});
```


JavaFXなどGUIアプリでメインスレッドからHTTPアクセスすると固まちゃう場合にはこっちを使いたい。
FunctionalInterfaceじゃないのでラムダ使えないのが辛い。

### JSONコンバーターをJacksonに変える
RetrofitはデフォルトでJSONコンバーターにGsonを使用する。依存ライブラリ的にJacksonの方が都合がいいって場合もあると思うが、RetrofitではJSONコンバーターを入れ替えることができる。

pom.xmlを以下のように書き換える

```xml
<dependency>
    <groupId>com.squareup.retrofit</groupId>
    <artifactId>retrofit</artifactId>
    <version>1.5.0</version>
    <exclusions>
        <exclusion>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>com.squareup.retrofit</groupId>
    <artifactId>converter-jackson</artifactId>
    <version>1.5.0</version>
</dependency>
```

コンバーターは`RestAdapter`に設定して差し替える。

```java
RestAdapter restAdapter = new RestAdapter.Builder()
        .setEndpoint("http://blog.ik.am/api/v1")
        .setConverter(new JacksonConverter())
        .build();
EntryRestRepository repository = restAdapter.create(EntryRestRepository.class);
```

Jacksonではおなじみの`ObjectMapper`も`JacksonConverter`に設定可能。

```java
ObjectMapper objectMapper = new ObjectMapper();

RestAdapter restAdapter = new RestAdapter.Builder()
        .setEndpoint("http://blog.ik.am/api/v1")
        .setConverter(new JacksonConverter(objectMapper))
        .build();
EntryRestRepository repository = restAdapter.create(EntryRestRepository.class);
```

GsonとJacksonの違いは色々あるけど、GsonだとJavaBeanには存在しないがJSONに存在するフィールドがあると無視するけど、Jacksonの方は`UnrecognizedPropertyException`をスローする。Gson側の挙動に合わせるには、以下のように`@JsonIgnoreProperties(ignoreUnknown = true)`をつける必要がある。

```java
package restclient;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@lombok.Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Entry {
    private Long entryId;
    private String title;
    private String contents;
    private String format;
}
```

クライアント側は使いたいフィールドだけ持てば良いのでこちらの方が良いかもしれない。ミスに気づきにくくなるかもだけど。

----
これから使っていきたいと思った。

JavaFXと相性が良い気がする。

JAX-RSのClientだと[ちょっと辛かった][3]。

コンバーターはProtocol Bufferにも対応しているみたい。


  [1]: /#/entries/176
  [2]: http://square.github.io/retrofit/
  [3]: /#/entries/210
