HandlerMethod evaluates ResponseStatus annotation for early caching

Issue: SPR-15227
This commit is contained in:
Juergen Hoeller 2017-04-08 15:56:05 +02:00
parent ff03b4a4ad
commit 5986f881d0
4 changed files with 82 additions and 81 deletions

View File

@ -64,7 +64,7 @@ public class HandlerMethod {
private final MethodParameter[] parameters; private final MethodParameter[] parameters;
private final HandlerMethod resolvedFromHandlerMethod; private HandlerMethod resolvedFromHandlerMethod;
/** /**
@ -79,7 +79,6 @@ public class HandlerMethod {
this.method = method; this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
this.parameters = initMethodParameters(); this.parameters = initMethodParameters();
this.resolvedFromHandlerMethod = null;
} }
/** /**
@ -95,7 +94,6 @@ public class HandlerMethod {
this.method = bean.getClass().getMethod(methodName, parameterTypes); this.method = bean.getClass().getMethod(methodName, parameterTypes);
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method); this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method);
this.parameters = initMethodParameters(); this.parameters = initMethodParameters();
this.resolvedFromHandlerMethod = null;
} }
/** /**
@ -113,7 +111,6 @@ public class HandlerMethod {
this.method = method; this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
this.parameters = initMethodParameters(); this.parameters = initMethodParameters();
this.resolvedFromHandlerMethod = null;
} }
/** /**
@ -158,14 +155,14 @@ public class HandlerMethod {
} }
/** /**
* Returns the bean for this handler method. * Return the bean for this handler method.
*/ */
public Object getBean() { public Object getBean() {
return this.bean; return this.bean;
} }
/** /**
* Returns the method for this handler method. * Return the method for this handler method.
*/ */
public Method getMethod() { public Method getMethod() {
return this.method; return this.method;
@ -189,21 +186,12 @@ public class HandlerMethod {
} }
/** /**
* Returns the method parameters for this handler method. * Return the method parameters for this handler method.
*/ */
public MethodParameter[] getMethodParameters() { public MethodParameter[] getMethodParameters() {
return this.parameters; return this.parameters;
} }
/**
* Return the HandlerMethod from which this HandlerMethod instance was
* resolved via {@link #createWithResolvedBean()}.
* @since 4.3
*/
public HandlerMethod getResolvedFromHandlerMethod() {
return this.resolvedFromHandlerMethod;
}
/** /**
* Return the HandlerMethod return type. * Return the HandlerMethod return type.
*/ */
@ -219,14 +207,14 @@ public class HandlerMethod {
} }
/** /**
* Returns {@code true} if the method return type is void, {@code false} otherwise. * Return {@code true} if the method return type is void, {@code false} otherwise.
*/ */
public boolean isVoid() { public boolean isVoid() {
return Void.TYPE.equals(getReturnType().getParameterType()); return Void.TYPE.equals(getReturnType().getParameterType());
} }
/** /**
* Returns a single annotation on the underlying method traversing its super methods * Return a single annotation on the underlying method traversing its super methods
* if no annotation can be found on the given method itself. * if no annotation can be found on the given method itself.
* <p>Also supports <em>merged</em> composed annotations with attribute * <p>Also supports <em>merged</em> composed annotations with attribute
* overrides as of Spring Framework 4.3. * overrides as of Spring Framework 4.3.
@ -248,6 +236,15 @@ public class HandlerMethod {
return AnnotatedElementUtils.hasAnnotation(this.method, annotationType); return AnnotatedElementUtils.hasAnnotation(this.method, annotationType);
} }
/**
* Return the HandlerMethod from which this HandlerMethod instance was
* resolved via {@link #createWithResolvedBean()}.
* @since 4.3
*/
public HandlerMethod getResolvedFromHandlerMethod() {
return this.resolvedFromHandlerMethod;
}
/** /**
* If the provided instance contains a bean name rather than an object instance, * If the provided instance contains a bean name rather than an object instance,
* the bean name is resolved before a {@link HandlerMethod} is created and returned. * the bean name is resolved before a {@link HandlerMethod} is created and returned.

View File

@ -28,8 +28,10 @@ import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.ResponseStatus;
/** /**
* Encapsulates information about a handler method consisting of a * Encapsulates information about a handler method consisting of a
@ -65,7 +67,11 @@ public class HandlerMethod {
private final MethodParameter[] parameters; private final MethodParameter[] parameters;
private final HandlerMethod resolvedFromHandlerMethod; private HttpStatus responseStatus;
private String responseStatusReason;
private HandlerMethod resolvedFromHandlerMethod;
/** /**
@ -80,7 +86,7 @@ public class HandlerMethod {
this.method = method; this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
this.parameters = initMethodParameters(); this.parameters = initMethodParameters();
this.resolvedFromHandlerMethod = null; evaluateResponseStatus();
} }
/** /**
@ -96,7 +102,7 @@ public class HandlerMethod {
this.method = bean.getClass().getMethod(methodName, parameterTypes); this.method = bean.getClass().getMethod(methodName, parameterTypes);
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method); this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method);
this.parameters = initMethodParameters(); this.parameters = initMethodParameters();
this.resolvedFromHandlerMethod = null; evaluateResponseStatus();
} }
/** /**
@ -114,7 +120,7 @@ public class HandlerMethod {
this.method = method; this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
this.parameters = initMethodParameters(); this.parameters = initMethodParameters();
this.resolvedFromHandlerMethod = null; evaluateResponseStatus();
} }
/** /**
@ -128,6 +134,8 @@ public class HandlerMethod {
this.method = handlerMethod.method; this.method = handlerMethod.method;
this.bridgedMethod = handlerMethod.bridgedMethod; this.bridgedMethod = handlerMethod.bridgedMethod;
this.parameters = handlerMethod.parameters; this.parameters = handlerMethod.parameters;
this.responseStatus = handlerMethod.responseStatus;
this.responseStatusReason = handlerMethod.responseStatusReason;
this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod; this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod;
} }
@ -143,6 +151,8 @@ public class HandlerMethod {
this.method = handlerMethod.method; this.method = handlerMethod.method;
this.bridgedMethod = handlerMethod.bridgedMethod; this.bridgedMethod = handlerMethod.bridgedMethod;
this.parameters = handlerMethod.parameters; this.parameters = handlerMethod.parameters;
this.responseStatus = handlerMethod.responseStatus;
this.responseStatusReason = handlerMethod.responseStatusReason;
this.resolvedFromHandlerMethod = handlerMethod; this.resolvedFromHandlerMethod = handlerMethod;
} }
@ -158,15 +168,27 @@ public class HandlerMethod {
return result; return result;
} }
private void evaluateResponseStatus() {
ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
if (annotation == null) {
annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
}
if (annotation != null) {
this.responseStatus = annotation.code();
this.responseStatusReason = annotation.reason();
}
}
/** /**
* Returns the bean for this handler method. * Return the bean for this handler method.
*/ */
public Object getBean() { public Object getBean() {
return this.bean; return this.bean;
} }
/** /**
* Returns the method for this handler method. * Return the method for this handler method.
*/ */
public Method getMethod() { public Method getMethod() {
return this.method; return this.method;
@ -190,18 +212,28 @@ public class HandlerMethod {
} }
/** /**
* Returns the method parameters for this handler method. * Return the method parameters for this handler method.
*/ */
public MethodParameter[] getMethodParameters() { public MethodParameter[] getMethodParameters() {
return this.parameters; return this.parameters;
} }
/** /**
* Return the HandlerMethod from which this HandlerMethod instance was * Return the specified response status, if any.
* resolved via {@link #createWithResolvedBean()}. * @since 4.3.8
* @see ResponseStatus#code()
*/ */
public HandlerMethod getResolvedFromHandlerMethod() { protected HttpStatus getResponseStatus() {
return this.resolvedFromHandlerMethod; return this.responseStatus;
}
/**
* Return the associated response status reason, if any.
* @since 4.3.8
* @see ResponseStatus#reason()
*/
protected String getResponseStatusReason() {
return this.responseStatusReason;
} }
/** /**
@ -219,14 +251,14 @@ public class HandlerMethod {
} }
/** /**
* Returns {@code true} if the method return type is void, {@code false} otherwise. * Return {@code true} if the method return type is void, {@code false} otherwise.
*/ */
public boolean isVoid() { public boolean isVoid() {
return Void.TYPE.equals(getReturnType().getParameterType()); return Void.TYPE.equals(getReturnType().getParameterType());
} }
/** /**
* Returns a single annotation on the underlying method traversing its super methods * Return a single annotation on the underlying method traversing its super methods
* if no annotation can be found on the given method itself. * if no annotation can be found on the given method itself.
* <p>Also supports <em>merged</em> composed annotations with attribute * <p>Also supports <em>merged</em> composed annotations with attribute
* overrides as of Spring Framework 4.2.2. * overrides as of Spring Framework 4.2.2.
@ -248,6 +280,14 @@ public class HandlerMethod {
return AnnotatedElementUtils.hasAnnotation(this.method, annotationType); return AnnotatedElementUtils.hasAnnotation(this.method, annotationType);
} }
/**
* Return the HandlerMethod from which this HandlerMethod instance was
* resolved via {@link #createWithResolvedBean()}.
*/
public HandlerMethod getResolvedFromHandlerMethod() {
return this.resolvedFromHandlerMethod;
}
/** /**
* If the provided instance contains a bean name rather than an object instance, * If the provided instance contains a bean name rather than an object instance,
* the bean name is resolved before a {@link HandlerMethod} is created and returned. * the bean name is resolved before a {@link HandlerMethod} is created and returned.

View File

@ -31,12 +31,10 @@ import reactor.core.publisher.Mono;
import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.BindingContext;
import org.springframework.web.reactive.HandlerResult; import org.springframework.web.reactive.HandlerResult;
@ -57,8 +55,6 @@ public class InvocableHandlerMethod extends HandlerMethod {
private static final Object NO_ARG_VALUE = new Object(); private static final Object NO_ARG_VALUE = new Object();
private HttpStatus responseStatus;
private List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(); private List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
@ -67,23 +63,12 @@ public class InvocableHandlerMethod extends HandlerMethod {
public InvocableHandlerMethod(HandlerMethod handlerMethod) { public InvocableHandlerMethod(HandlerMethod handlerMethod) {
super(handlerMethod); super(handlerMethod);
initResponseStatus();
} }
public InvocableHandlerMethod(Object bean, Method method) { public InvocableHandlerMethod(Object bean, Method method) {
super(bean, method); super(bean, method);
initResponseStatus();
} }
private void initResponseStatus() {
ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
if (annotation == null) {
annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
}
if (annotation != null) {
this.responseStatus = annotation.code();
}
}
/** /**
* Configure the argument resolvers to use to use for resolving method * Configure the argument resolvers to use to use for resolving method
@ -132,8 +117,9 @@ public class InvocableHandlerMethod extends HandlerMethod {
try { try {
Object value = doInvoke(args); Object value = doInvoke(args);
HandlerResult result = new HandlerResult(this, value, getReturnType(), bindingContext); HandlerResult result = new HandlerResult(this, value, getReturnType(), bindingContext);
if (this.responseStatus != null) { HttpStatus status = getResponseStatus();
exchange.getResponse().setStatusCode(this.responseStatus); if (status != null) {
exchange.getResponse().setStatusCode(status);
} }
return Mono.just(result); return Mono.just(result);
} }

View File

@ -25,7 +25,6 @@ import java.util.concurrent.Callable;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -59,11 +58,6 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
private static final Method CALLABLE_METHOD = ClassUtils.getMethod(Callable.class, "call"); private static final Method CALLABLE_METHOD = ClassUtils.getMethod(Callable.class, "call");
private HttpStatus responseStatus;
private String responseReason;
private HandlerMethodReturnValueHandlerComposite returnValueHandlers; private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
@ -72,7 +66,6 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
*/ */
public ServletInvocableHandlerMethod(Object handler, Method method) { public ServletInvocableHandlerMethod(Object handler, Method method) {
super(handler, method); super(handler, method);
initResponseStatus();
} }
/** /**
@ -80,21 +73,9 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
*/ */
public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) { public ServletInvocableHandlerMethod(HandlerMethod handlerMethod) {
super(handlerMethod); super(handlerMethod);
initResponseStatus();
} }
private void initResponseStatus() {
ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
if (annotation == null) {
annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
}
if (annotation != null) {
this.responseStatus = annotation.code();
this.responseReason = annotation.reason();
}
}
/** /**
* Register {@link HandlerMethodReturnValueHandler} instances to use to * Register {@link HandlerMethodReturnValueHandler} instances to use to
* handle return values. * handle return values.
@ -118,12 +99,12 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
setResponseStatus(webRequest); setResponseStatus(webRequest);
if (returnValue == null) { if (returnValue == null) {
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true); mavContainer.setRequestHandled(true);
return; return;
} }
} }
else if (StringUtils.hasText(this.responseReason)) { else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true); mavContainer.setRequestHandled(true);
return; return;
} }
@ -145,17 +126,21 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
* Set the response status according to the {@link ResponseStatus} annotation. * Set the response status according to the {@link ResponseStatus} annotation.
*/ */
private void setResponseStatus(ServletWebRequest webRequest) throws IOException { private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
if (this.responseStatus == null) { HttpStatus status = getResponseStatus();
if (status == null) {
return; return;
} }
if (StringUtils.hasText(this.responseReason)) {
webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason); String reason = getResponseStatusReason();
if (StringUtils.hasText(reason)) {
webRequest.getResponse().sendError(status.value(), reason);
} }
else { else {
webRequest.getResponse().setStatus(this.responseStatus.value()); webRequest.getResponse().setStatus(status.value());
} }
// To be picked up by RedirectView // To be picked up by RedirectView
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus); webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
} }
/** /**
@ -167,13 +152,6 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
return webRequest.isNotModified(); return webRequest.isNotModified();
} }
/**
* Does this method have the response status instruction?
*/
private boolean hasResponseStatus() {
return (this.responseStatus != null);
}
private String getReturnValueHandlingErrorMessage(String message, Object returnValue) { private String getReturnValueHandlingErrorMessage(String message, Object returnValue) {
StringBuilder sb = new StringBuilder(message); StringBuilder sb = new StringBuilder(message);
if (returnValue != null) { if (returnValue != null) {