前言
用于处理异步请求时,将返回值直接写入响应流中,而不用一直持有Servlet容器线程。
一、StreamingResponseBody
@FunctionalInterface
public interface StreamingResponseBody {
/**
* A callback for writing to the response body.
* @param outputStream the stream for the response body
* @throws IOException an exception while writing
*/
void writeTo(OutputStream outputStream) throws IOException;
}
只有一个方法writeTo( ),
二、示例
1、返回StreamingResponseBody
@GetMapping(value = "/test", produces = {
MediaType.APPLICATION_JSON_VALUE})
public StreamingResponseBody test() throws ValidDataException {
Test test = new Test();
test.setId(1);
test.setName("test1");
return out -> {
try (ObjectOutputStream oos = new ObjectOutputStream(out)) {
oos.writeBytes(new Gson().toJson(test));
}
};
}
2、返回ResponseEntity
@GetMapping(value = "/test", produces = {
MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<StreamingResponseBody> test() throws ValidDataException {
Test test = new Test();
test.setId(1);
test.setName("test1");
StreamingResponseBody responseBody = out -> {
try (ObjectOutputStream oos = new ObjectOutputStream(out)) {
oos.writeBytes(new Gson().toJson(test));
}
};
return ResponseEntity(responseBody, HttpStatus.OK);
三、原理
1、StreamingResponseBodyReturnValueHandler
@Override
public boolean supportsReturnType(MethodParameter returnType) {
//StreamingResponseBody类型
if (StreamingResponseBody.class.isAssignableFrom(returnType.getParameterType())) {
return true;
}
//ResponseEntity类型,body是StreamingResponseBody类型
else if (ResponseEntity.class.isAssignableFrom(returnType.getParameterType())) {
Class<?> bodyType = ResolvableType.forMethodParameter(returnType).getGeneric().resolve();
return (bodyType != null && StreamingResponseBody.class.isAssignableFrom(bodyType));
}
return false;
}
@Override
@SuppressWarnings("resource")
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
mavContainer.setRequestHandled(true);
return;
}
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
Assert.state(response != null, "No HttpServletResponse");
ServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
if (returnValue instanceof ResponseEntity) {
ResponseEntity<?> responseEntity = (ResponseEntity<?>) returnValue;
//状态码
response.setStatus(responseEntity.getStatusCodeValue());
//响应头
outputMessage.getHeaders().putAll(responseEntity.getHeaders());
//响应数据
returnValue = responseEntity.getBody();
if (returnValue == null) {
mavContainer.setRequestHandled(true);
outputMessage.flush();
return;
}
}
ServletRequest request = webRequest.getNativeRequest(ServletRequest.class);
Assert.state(request != null, "No ServletRequest");
ShallowEtagHeaderFilter.disableContentCaching(request);
Assert.isInstanceOf(StreamingResponseBody.class, returnValue, "StreamingResponseBody expected");
StreamingResponseBody streamingBody = (StreamingResponseBody) returnValue;
//创建StreamingResponseBodyTask
Callable<Void> callable = new StreamingResponseBodyTask(outputMessage.getBody(), streamingBody);
//调用异步线程池执行任务
WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(callable, mavContainer);
}
2、StreamingResponseBodyTask
用于回调StreamingResponseBody 的writeTo( )方法
private static class StreamingResponseBodyTask implements Callable<Void> {
private final OutputStream outputStream;
private final StreamingResponseBody streamingBody;
public StreamingResponseBodyTask(OutputStream outputStream, StreamingResponseBody streamingBody) {
this.outputStream = outputStream;
this.streamingBody = streamingBody;
}
@Override
public Void call() throws Exception {
this.streamingBody.writeTo(this.outputStream);
this.outputStream.flush();
return null;
}
}
}
3、WebAsyncUtils.getAsyncManager(webRequest)
从request中获取异步执行管理器
public static WebAsyncManager getAsyncManager(WebRequest webRequest) {
int scope = RequestAttributes.SCOPE_REQUEST;
WebAsyncManager asyncManager = null;
//public static final String WEB_ASYNC_MANAGER_ATTRIBUTE = WebAsyncManager.class.getName() + ".WEB_ASYNC_MANAGER";
Object asyncManagerAttr = webRequest.getAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE, scope);
if (asyncManagerAttr instanceof WebAsyncManager) {
asyncManager = (WebAsyncManager) asyncManagerAttr;
}
if (asyncManager == null) {
//不存在时创建一个新的WebAsyncManager
asyncManager = new WebAsyncManager();
webRequest.setAttribute(WEB_ASYNC_MANAGER_ATTRIBUTE, asyncManager, scope);
}
return asyncManager;
}
4、startCallableProcessing
将callable封装成WebAsyncTask
public void startCallableProcessing(Callable<?> callable, Object... processingContext) throws Exception {
Assert.notNull(callable, "Callable must not be null");
startCallableProcessing(new WebAsyncTask(callable), processingContext);
}