---
title: SpringMVC 4.1のProtocol Buffers対応を試す
tags: ["Protocol Buffers", "Spring", "Spring MVC"]
categories: ["Programming", "Java", "org", "springframework", "web"]
date: 2014-10-11T15:11:59Z
updated: 2014-10-11T15:11:59Z
---

Spring MVC 4.1から対応したGoogleの[ProtocolBuffers](https://developers.google.com/protocol-buffers/)対応を試してみます。(4.1.0だと[バグって動きません](https://jira.spring.io/browse/SPR-12229))

Spring 4.1に対応した、Spring Boot 1.2(M2)を使っています。

Protoファイルは[チュートリアル](https://developers.google.com/protocol-buffers/docs/javatutorial)のものを使います。

Controllerの書き方はJSONというかRESTの場合と同じです。

```
package com.example.tutorial;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AddressBookController {
    @RequestMapping("person")
    AddressBookProtos.Person person() {
        return AddressBookProtos.Person.newBuilder()
                .setId(1234)
                .setName("John Doe")
                .setEmail("jdoe@example.com")
                .addPhone(
                        AddressBookProtos.Person.PhoneNumber.newBuilder()
                                .setNumber("555-4321")
                                .setType(AddressBookProtos.Person.PhoneType.HOME))
                .build();
    }
}
```

Spring側の対応としては、`@RestController`を使ったときに通る`org.springframework.http.converter.HttpMessageConverter`のProtobuf実装(`org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter`)を追加しています。

JavaConfigにこの`HttpMessageConverter`を追加します。

```
package com.example.tutorial;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;

@Configuration
public class AppConfig {
    @Bean
    ProtobufHttpMessageConverter protobufHttpMessageConverter() {
        return new ProtobufHttpMessageConverter((extensionRegistry) -> {
        });
    }
}
```

あとはいつも通りSpring Bootのエントリポイントを作るだけ

```
package com.example.tutorial;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

@EnableAutoConfiguration
@ComponentScan
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
```
この`App`を実行して、アクセスすると

```
$ curl http://localhost:8080/person

John Doe�	jdoe@example.com"

555-4321
```

こんな感じです。


テストはこんな感じ。(`RestTemplate`使うので十分だった・・)

```
package com.example.tutorial;

import com.jayway.restassured.RestAssured;
import com.jayway.restassured.response.ResponseBodyData;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import static com.jayway.restassured.RestAssured.when;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = App.class)
@WebAppConfiguration
@IntegrationTest({"server.port:0"})
public class AddressBookControllerTest {
    @Value("${local.server.port}")
    int port;

    @Before
    public void setUp() throws Exception {
        RestAssured.port = port;
    }

    @Test
    public void testPerson() throws Exception {
        ResponseBodyData body = when().get("/person")
                .then()
                .extract()
                .body();
        AddressBookProtos.Person person = AddressBookProtos.Person.parseFrom(body.asInputStream());

        assertThat(person.getName(), is("John Doe"));
        assertThat(person.getId(), is(1234));
        assertThat(person.getEmail(), is("jdoe@example.com"));
        assertThat(person.getPhoneList(), hasSize(1));
        assertThat(person.getPhone(0).getNumber(), is("555-4321"));
        assertThat(person.getPhone(0).getType(), is(AddressBookProtos.Person.PhoneType.HOME));
    }
}
```

せっかくなのでPythonからもアクセスしてみます。

```
import httplib
import addressbook_pb2

conn = httplib.HTTPConnection("localhost", 8080)
conn.request("GET", "/person")
res = conn.getresponse()
body = res.read()
conn.close()

person = addressbook_pb2.Person()
person.ParseFromString(body)
print person
```

Python2系ですいません・・

```
$ python test.py 
name: "John Doe"
id: 1234
email: "jdoe@example.com"
phone {
  number: "555-4321"
  type: HOME
}
```

いけました。

HTTP経由だとJSONに比べてどのくらいメリットあるんだろう・・・

ソースコードは[Github](https://github.com/making/spring-mvc-tutorial-protobuf)に上げました。
