当前位置: 首页 > 知识库问答 >
问题:

从Spring REST控制器返回流

况唯
2023-03-14

如果可以从SpringRestController返回Stream,我很好奇

@RestController
public class X {
  @RequestMapping(...)
  public Stream<?> getAll() { ... }
}

这样做可以吗?我试过了,Spring返回的不是流的值。

我应该继续返回列表吗


共有2个答案

洪伟彦
2023-03-14
匿名用户

您可以在Spring 5.0 / WebFlux 中流式传输实体。

看一下这个示例反应式Rest控制器(< code > spring . main . web-application-type:" REACTIVE " ):

@RestController
public class XService {

    class XDto{
        final int x;
        public XDto(int x) {this.x = x;}
    }

    Stream<XDto> produceX(){
        return IntStream.range(1,10).mapToObj(i -> {
            System.out.println("produce "+i);
            try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
            return new XDto(i);
        });
    }

    // stream of Server-Sent Events (SSE)
    @GetMapping(value = "/api/x/sse", 
    produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<XDto> getXSse() {
        return Flux.fromStream(produceX());
    }

    // stream of JSON lines
    @GetMapping(value = "/api/x/json-stream", 
    produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
    public Flux<XDto> getAllJsonStream() {
        return Flux.fromStream(produceX());
    }

    // same as List<XDto> - blocking JSON list
    @GetMapping(value = "/api/x/json-list", 
    produces = MediaType.APPLICATION_JSON_VALUE)
    public Flux<XDto> getAll() {
        return Flux.fromStream(produceX());
    }
}

Spring Framework 5.0-WebFlux:

Spring的反应式堆栈Web框架是5.0中新增的,是完全反应式和非阻塞的。它适用于具有少量线程的事件循环样式处理。

服务器发送的事件(SSE):

服务器发送事件是描述一旦建立了初始客户端连接,服务器如何向客户端发起数据传输的标准。

WebSocket与服务器发送事件/事件源

祁修平
2023-03-14
匿名用户

这也可以用Spring MVC控制器来完成,但是有一些问题:Spring数据JPA存储库的限制,数据库是否支持可保持游标(ResultSet Holdability)以及Jackson的版本。

我很难理解的关键概念是,Java 8 Stream返回一系列在终端操作中执行的函数,因此数据库必须在执行终端操作的上下文中可访问。

Spring数据 JPA 限制

我发现Spring数据JPA文档没有为Java 8流提供足够的细节。看起来你可以简单地声明

@Query("select mo from MyObject mo where mo.foo.id in :fooIds")
Stream<MyObject> readAllByFooIn(@Param("fooIds") Long[] fooIds);

可保持光标

如果您有一个支持可持有游标的数据库,则在提交事务后可以访问结果集。这很重要,因为我们通常用< code>@Transactional来注释我们的< code>@Service类方法,所以如果您的数据库支持可保持游标,则可以在服务方法返回后(即在< code>@Controller方法中)访问< code>ResultSet。如果数据库不支持可持有游标,例如MySQL,您需要将< code>@Transaction注释添加到控制器的< code>@RequestMapping方法中。

所以现在ResultSet可以在@Service方法之外访问,对吗?这同样取决于可保留性。对于MySQL,它只能在@Transactional方法中访问,因此以下方法将起作用(尽管违背了使用Java8 Streams的全部目的):

@Transaction @RequestMapping(...)
public List<MyObject> getAll() {
   try(Stream<MyObject> stream = service.streamAll) {
        return stream.collect(Collectors.toList())
    };
}

但不是

@Transaction @RequestMapping
public Stream<MyObject> getAll() {
    return service.streamAll;
}

因为终端操作符不在您的@Controller中,它在控制器方法返回后发生在Spring中。

在不支持可保留游标的情况下将流序列化为JSON

要将流序列化为JSON而不使用可保留游标,请将<code>HttpServletResponse,但在2.8.x中,您必须使用流的迭代器:

@RequestMapping(...)
@Transactional
public void getAll(HttpServletResponse response) throws IOException {
    try(final Stream<MyObject> stream = service.streamAll()) {
        final Writer writer = new BufferedWriter(new OutputStreamWriter(response.getOutputStream()));
        new ObjectMapper().writerFor(Iterator.class).writeValue(writer, stream.iterator());
    }
}

下一步

我的下一步是尝试在可调用的WebAsyncTask中重构它,并将JSON序列化移到服务中。

引用

  • 请务必阅读Marko Topolnik的博客文章https://www.airpair.com/java/posts/spring-streams-memory-efficiency没有它,我就不知道从哪里开始
  • MySQL

 类似资料:
  • 我得到这个错误消息: 无法写入JSON: 未找到用于java.io.FileDescriptor类的序列化程序,也未发现用于创建BeanSerializer的属性 (为了避免异常,禁用SerializationFeature.fail_on_empty_beans)) (通过引用链: org.springframework.core.io.filesystemResource[\“outputSt

  • 我正在使用Spring形式。我只需要得到Staemap作为响应,但我得到的是整个jsp页面作为响应。

  • 你好,我是拉威尔的新手,也许这对你们来说太傻了。在laravel 8中,路由web。php我创建了一条如下的路线: 我想问的是,我们也可以从回调视图返回控制器吗?所以在路由 /editprofile中,第二个参数不是'App\Http\Controller\SiteController@edit_profile',而是一个回调函数,如路由'/home'。 但是它返回错误哈哈。假设我不想用__con

  • 我无法完成的任务是将参数从一个场景传递到另一个场景并返回。 或者换句话说:主控制器加载子控制器的fxml,将对象传递给子控制器,切换场景。不得有两扇开着的窗户。工作完成后,副控制器将场景切换回主控制器,并将一些对象传递回主控制器。这就是我失败的地方。 这个问题和这个问题很相似但仍然没有答案。传递参数JavaFX FXML在注释中也提到了: 有没有人知道如何在没有外部库的情况下实现这一点?

  • 目标是打印标签的当前文本。标签在整个程序中不断变化,使用当前的方法,我只能检索标签的初始化值。 我用来检索控制器实例的内容:

  • 我试图为web api控制器中的GET方法返回一个状态代码304 not modified。 我成功的唯一方法是这样的: 这里的问题是,它并不是一个例外,它只是没有被修改,所以客户端缓存是可以的。我还希望返回类型为User(正如所有web api示例使用GET显示的那样),而不是返回HttpResponseMessage或类似的内容。