Add AsyncHandlerMethodReturnValueHandler
Before this change HandlerMethodReturnValueHandler's were invoked in a specific order (type-based, annotation-based, custom). However handlers that deal with asynchronous return value handling need to always be considered first. This affects custom handlers in particular since they are normally ordered last. This change introduces an AsyncHandlerMethodReturnValueHandler sub-interface with a single method to determine if the return value is asynchronous and if it is to look for a matching handler only among those that are of type AsyncHandlerMethodReturnValueHandler. Issue: SPR-13083
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.web.method.support;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
|
||||
/**
|
||||
* A {@link HandlerMethodReturnValueHandler} that handles return values that
|
||||
* represent asynchronous computation. Such handlers need to be invoked with
|
||||
* precedence over other handlers that might otherwise match the return value
|
||||
* type -- e.g. a method that returns a Promise type that is also annotated with
|
||||
* {@code @ResponseBody}.
|
||||
*
|
||||
* <p>In {@link #handleReturnValue}, implementations of this class should create
|
||||
* a {@link org.springframework.web.context.request.async.DeferredResult} or
|
||||
* adapt to it and then invoke {@code WebAsyncManager} to start async processing.
|
||||
* For example:
|
||||
* <pre>
|
||||
* DeferredResult<?> deferredResult = (DeferredResult<?>) returnValue;
|
||||
* WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(deferredResult, mavContainer);
|
||||
* </pre>
|
||||
* the return value to a DeferredResult
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.2
|
||||
*/
|
||||
public interface AsyncHandlerMethodReturnValueHandler extends HandlerMethodReturnValueHandler {
|
||||
|
||||
/**
|
||||
* Whether the given return value represents asynchronous computation.
|
||||
* @param returnValue the return value
|
||||
* @param returnType the return type
|
||||
* @return {@code true} if the return value is asynchronous.
|
||||
*/
|
||||
boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType);
|
||||
|
||||
}
|
||||
@@ -34,7 +34,7 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.1
|
||||
*/
|
||||
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
|
||||
public class HandlerMethodReturnValueHandlerComposite implements AsyncHandlerMethodReturnValueHandler {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@@ -58,6 +58,15 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
|
||||
return getReturnValueHandler(returnType) != null;
|
||||
}
|
||||
|
||||
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
|
||||
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
|
||||
if (handler.supportsReturnType(returnType)) {
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it.
|
||||
* @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
|
||||
@@ -66,32 +75,41 @@ public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodRe
|
||||
public void handleReturnValue(Object returnValue, MethodParameter returnType,
|
||||
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
|
||||
|
||||
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
|
||||
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
|
||||
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
|
||||
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a registered {@link HandlerMethodReturnValueHandler} that supports the given return type.
|
||||
*/
|
||||
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
|
||||
for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" +
|
||||
returnType.getGenericParameterType() + "]");
|
||||
private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {
|
||||
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
|
||||
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
|
||||
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
|
||||
continue;
|
||||
}
|
||||
if (returnValueHandler.supportsReturnType(returnType)) {
|
||||
return returnValueHandler;
|
||||
if (handler.supportsReturnType(returnType)) {
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAsyncReturnValue(Object value, MethodParameter returnType) {
|
||||
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
|
||||
if (handler instanceof AsyncHandlerMethodReturnValueHandler) {
|
||||
if (((AsyncHandlerMethodReturnValueHandler) handler).isAsyncReturnValue(value, returnType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given {@link HandlerMethodReturnValueHandler}.
|
||||
*/
|
||||
public HandlerMethodReturnValueHandlerComposite addHandler(HandlerMethodReturnValueHandler handler) {
|
||||
returnValueHandlers.add(handler);
|
||||
this.returnValueHandlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user