Spring REST API 中的二進制數據格式

Data,REST,Spring
Remote
0
04:01 PM · Dec 01 ,2025

1. 概述

雖然JSON和XML在REST API的數據傳輸格式中非常流行,但它們並不是唯一的選擇。

存在許多其他格式,具有不同的序列化速度和序列化數據大小。

在本文中,我們將探討如何配置 Spring REST機制以使用二進制數據格式 – 我們將通過Kryo進行説明。

此外,我們還將展示如何通過添加對Google Protocol buffers的支持來支持多種數據格式。

2. HttpMessageConverter

HttpMessageConverter 接口基本上是 Spring 的公共 API,用於 REST 數據格式的轉換。

有不同的方法可以指定所需的轉換器。這裏我們實現了 WebMvcConfigurer 並在覆蓋的 configureMessageConverters 方法中顯式提供我們想要使用的轉換器:

@Configuration
@EnableWebMvc
@ComponentScan({ "com.baeldung.web" })
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        //...
    }
}

3. Kryo

3.1. Kryo Overview and Maven

Kryo 是一種二進制編碼格式,它提供良好的序列化和反序列化速度,以及與文本格式相比更小的傳輸數據大小。

雖然在理論上它可以用於不同類型的系統之間的數據傳輸,但它主要設計用於與 Java 組件一起使用。

我們添加了必要的 Kryo 庫,如下所示 Maven 依賴項:


<dependency>
    <groupId>com.esotericsoftware</groupId>
    <artifactId>kryo</artifactId>
    <version>4.0.0</version>
</dependency>

要查看 Kryo 的最新版本,您可以在此處查看

3.2. Kryo in Spring REST

為了利用 Kryo 作為數據傳輸格式,我們創建一個自定義 HttpMessageConverter 並實現必要的序列化和反序列化邏輯。 此外,我們定義了自定義 HTTP 標頭用於 Kryo: application/x-kryo。 下面是一個完整的簡化版工作示例,我們用於演示目的:

public class KryoHttpMessageConverter extends AbstractHttpMessageConverter<Object> {

    public static final MediaType KRYO = new MediaType("application", "x-kryo");

    private static final ThreadLocal<Kryo> kryoThreadLocal = new ThreadLocal<Kryo>() {
        @Override
        protected Kryo initialValue() {
            Kryo kryo = new Kryo();
            kryo.register(Foo.class, 1);
            return kryo;
        }
    };

    public KryoHttpMessageConverter() {
        super(KRYO);
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return Object.class.isAssignableFrom(clazz);
    }

    @Override
    protected Object readInternal(
      Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException {
        Input input = new Input(inputMessage.getBody());
        return kryoThreadLocal.get().readClassAndObject(input);
    }

    @Override
    protected void writeInternal(
      Object object, HttpOutputMessage outputMessage) throws IOException {
        Output output = new Output(outputMessage.getBody());
        kryoThreadLocal.get().writeClassAndObject(output, object);
        output.flush();
    }

    @Override
    protected MediaType getDefaultContentType(Object object) {
        return KRYO;
    }
}

注意我們使用了 ThreadLocal,這僅僅是因為 Kryo 實例的創建可能很昂貴,我們希望儘可能地重用它們。

控制器方法很直觀(注意沒有必要為任何特定協議的數據類型而進行自定義),我們使用簡單的 Foo DTO:

@RequestMapping(method = RequestMethod.GET, value = "/foos/{id}")
@ResponseBody
public Foo findById(@PathVariable long id) {
    return fooRepository.findById(id);
}

以及一個快速測試,以證明我們已正確地連接一切:

RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(Arrays.asList(new KryoHttpMessageConverter()));

HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(KryoHttpMessageConverter.KRYO));
HttpEntity<String> entity = new HttpEntity<String>(headers);

ResponseEntity<Foo> response = restTemplate.exchange("http://localhost:8080/spring-rest/foos/{id}",
  HttpMethod.GET, entity, Foo.class, "1");
Foo resource = response.getBody();

assertThat(resource, notNullValue());

4. 支持多種數據格式

通常,您需要支持多種數據格式,以供同一服務使用。客户端指定所需的數據格式在 HTTP 頭部中,並調用相應的消息轉換器來序列化數據。

通常,您只需要註冊另一個轉換器,即可自動處理。Spring 根據 頭部的值和支持的媒體類型,自動選擇合適的轉換器。

例如,要添加對 JSON 和 Kryo 的支持,請註冊

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
    messageConverters.add(new MappingJackson2HttpMessageConverter());
    messageConverters.add(new KryoHttpMessageConverter());
    super.configureMessageConverters(messageConverters);
}

現在,假設我們還要將 Google Protocol Buffer 添加到列表中。對於此示例,假設有一個類 ,該類已使用 編譯器根據以下 文件生成:

package baeldung;
option java_package = "com.baeldung.web.dto";
option java_outer_classname = "FooProtos";
message Foo {
    required int64 id = 1;
    required string name = 2;
}

Spring 提供了對 Protocol Buffer 的內置支持。只需將 添加到支持的轉換器列表中即可。

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
    messageConverters.add(new MappingJackson2HttpMessageConverter());
    messageConverters.add(new KryoHttpMessageConverter());
    messageConverters.add(new ProtobufHttpMessageConverter());
}

但是,我們必須定義一個返回 實例的方法,該方法用於處理 Protocol Buffer。

有兩件可以解決歧義的方法。第一種方法是使用不同的 URL 來處理 Protocol Buffer 和其他格式。例如,對於 Protocol Buffer:

@RequestMapping(method = RequestMethod.GET, value = "/fooprotos/{id}")
@ResponseBody
public FooProtos.Foo findProtoById(@PathVariable long id) { … }

以及對於其他格式:

@RequestMapping(method = RequestMethod.GET, value = "/foos/{id}")
@ResponseBody
public Foo findById(@PathVariable long id) { … }

請注意,對於 Protocol Buffer,我們使用 ,對於其他格式,我們使用

第二種 – 也是更好的方法是使用相同的 URL,但明確地在請求映射中指定生成的格式

@RequestMapping(
  method = RequestMethod.GET, 
  value = "/foos/{id}", 
  produces = { "application/x-protobuf" })
@ResponseBody
public FooProtos.Foo findProtoById(@PathVariable long id) { … }

請注意,通過在 註解屬性中指定媒體類型,我們向底層的 Spring 機制提供有關根據 頭部的值,選擇哪個映射的信息,因此對於 URL,沒有歧義。

第二種方法使我們能夠為客户端提供所有數據格式的統一和一致的 REST API。

最後,如果您想更深入地瞭解使用 Protocol Buffer 與 Spring REST API 的使用,請查看參考文章。

5. 註冊額外消息轉換器

請務必注意,您在覆蓋 configureMessageConverters 方法時,會丟失所有默認消息轉換器。 只有您提供的轉換器才會使用。

雖然在某些情況下這正是您想要的,但在大多數情況下,您可能只想添加新的轉換器,同時保留默認的轉換器,這些轉換器已經能夠處理標準數據格式,如 JSON。 要實現這一點,請覆蓋 extendMessageConverters 方法:

@Configuration
@EnableWebMvc
@ComponentScan({ "com.baeldung.web" })
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        messageConverters.add(new ProtobufHttpMessageConverter());
        messageConverters.add(new KryoHttpMessageConverter());
    }
}

6. 結論

在本教程中,我們探討瞭如何在 Spring MVC 中輕鬆使用任何數據傳輸格式,並通過 Kryo 作為一個例子進行了驗證。

我們還展示瞭如何添加對多種格式的支持,以便不同的客户端能夠使用不同的格式。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.