从 Spring REST 控制器返回流
如果有可能从春天返回一个Stream
RestController
@RestController
public class X {
@RequestMapping(...)
public Stream<?> getAll() { ... }
}
做这样的事情可以吗?我试过了,Spring返回了流值以外的其他东西。
我应该继续返回吗?List<?>
如果有可能从春天返回一个Stream
RestController
@RestController
public class X {
@RequestMapping(...)
public Stream<?> getAll() { ... }
}
做这样的事情可以吗?我试过了,Spring返回了流值以外的其他东西。
我应该继续返回吗?List<?>
这也可以通过Spring MVC Controller来完成,但是存在一些问题:Spring Data JPA存储库中的限制,数据库是否支持可保持游标(ResultSet Holdability)和Jackson的版本。
我很难理解的关键概念是,Java 8 Stream返回一系列在终端操作中执行的函数,因此数据库必须在执行终端操作的上下文中可访问。
春季数据 JPA 限制
我发现Spring Data JPA文档没有为Java 8 Streams提供足够的细节。看起来你可以简单地声明,但我需要注释方法才能使其工作。我也无法使用JPA标准API。因此,我不得不满足于一个硬编码的查询,例如:Stream<MyObject> readAll()
@Query
Specification
@Query("select mo from MyObject mo where mo.foo.id in :fooIds")
Stream<MyObject> readAllByFooIn(@Param("fooIds") Long[] fooIds);
可保持光标
如果具有支持可保留游标的数据库,则在提交事务后可以访问结果集。这很重要,因为我们通常使用 对类方法进行注释,因此,如果您的数据库支持可保持游标,则可以在服务方法返回后(即在方法中)访问。如果数据库不支持可保存的游标,例如MySQL,则需要将注释添加到控制器的方法中。@Service
@Transactional
ResultSet
@Controller
@Transaction
@RequestMapping
所以现在 ResultSet 可以在方法之外访问,对吧?这同样取决于可持性。对于MySQL,它只能在方法中访问,因此以下内容将起作用(尽管违背了使用Java 8 Streams的整个目的):@Service
@Transactional
@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
将流序列化为 JSON,而不支持“可保留游标”
若要在没有可保留游标的情况下将流序列化为 JSON,请添加到控制器方法,获取输出流并用于写入流。使用 FasterXML 3.x,您可以调用 ,但对于 2.8.x,您必须使用流的迭代器:HttpServletResponse response
ObjectMapper
ObjectMapper().writeValue(writer, stream)
@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());
}
}
四. 今后的步骤
我的下一步是尝试在 中重构它,并将 JSON 序列化移动到服务中。Callable
WebAsyncTask
引用
useCursorFetch=true
您可以在Spring 5.0 / WebFlux中流式传输实体。
看看这个例子 反应式休息控制器 ():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中新增的,它是完全响应式和非阻塞的。它适用于具有少量线程的事件循环样式处理。
服务器发送的事件是一种标准,用于描述一旦建立初始客户端连接,服务器如何启动向客户端的数据传输。