From 5986f881d02e9281862844fb9f2a131be71a2f10 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 8 Apr 2017 15:56:05 +0200 Subject: [PATCH] HandlerMethod evaluates ResponseStatus annotation for early caching Issue: SPR-15227 --- .../messaging/handler/HandlerMethod.java | 33 +++++----- .../web/method/HandlerMethod.java | 66 +++++++++++++++---- .../result/method/InvocableHandlerMethod.java | 20 +----- .../ServletInvocableHandlerMethod.java | 44 ++++--------- 4 files changed, 82 insertions(+), 81 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java index b83576ecb76..0cd4d91e16c 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java @@ -64,7 +64,7 @@ public class HandlerMethod { private final MethodParameter[] parameters; - private final HandlerMethod resolvedFromHandlerMethod; + private HandlerMethod resolvedFromHandlerMethod; /** @@ -79,7 +79,6 @@ public class HandlerMethod { this.method = method; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = initMethodParameters(); - this.resolvedFromHandlerMethod = null; } /** @@ -95,7 +94,6 @@ public class HandlerMethod { this.method = bean.getClass().getMethod(methodName, parameterTypes); this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method); this.parameters = initMethodParameters(); - this.resolvedFromHandlerMethod = null; } /** @@ -113,7 +111,6 @@ public class HandlerMethod { this.method = method; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); 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() { return this.bean; } /** - * Returns the method for this handler method. + * Return the method for this handler method. */ public Method getMethod() { 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() { 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. */ @@ -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() { 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. *

Also supports merged composed annotations with attribute * overrides as of Spring Framework 4.3. @@ -248,6 +236,15 @@ public class HandlerMethod { 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, * the bean name is resolved before a {@link HandlerMethod} is created and returned. diff --git a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java index d6cffd1d11c..d1541b5d8f4 100644 --- a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java +++ b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java @@ -28,8 +28,10 @@ import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.SynthesizingMethodParameter; +import org.springframework.http.HttpStatus; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.web.bind.annotation.ResponseStatus; /** * Encapsulates information about a handler method consisting of a @@ -65,7 +67,11 @@ public class HandlerMethod { 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.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = initMethodParameters(); - this.resolvedFromHandlerMethod = null; + evaluateResponseStatus(); } /** @@ -96,7 +102,7 @@ public class HandlerMethod { this.method = bean.getClass().getMethod(methodName, parameterTypes); this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(this.method); this.parameters = initMethodParameters(); - this.resolvedFromHandlerMethod = null; + evaluateResponseStatus(); } /** @@ -114,7 +120,7 @@ public class HandlerMethod { this.method = method; this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); this.parameters = initMethodParameters(); - this.resolvedFromHandlerMethod = null; + evaluateResponseStatus(); } /** @@ -128,6 +134,8 @@ public class HandlerMethod { this.method = handlerMethod.method; this.bridgedMethod = handlerMethod.bridgedMethod; this.parameters = handlerMethod.parameters; + this.responseStatus = handlerMethod.responseStatus; + this.responseStatusReason = handlerMethod.responseStatusReason; this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod; } @@ -143,6 +151,8 @@ public class HandlerMethod { this.method = handlerMethod.method; this.bridgedMethod = handlerMethod.bridgedMethod; this.parameters = handlerMethod.parameters; + this.responseStatus = handlerMethod.responseStatus; + this.responseStatusReason = handlerMethod.responseStatusReason; this.resolvedFromHandlerMethod = handlerMethod; } @@ -158,15 +168,27 @@ public class HandlerMethod { 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() { return this.bean; } /** - * Returns the method for this handler method. + * Return the method for this handler method. */ public Method getMethod() { 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() { return this.parameters; } /** - * Return the HandlerMethod from which this HandlerMethod instance was - * resolved via {@link #createWithResolvedBean()}. + * Return the specified response status, if any. + * @since 4.3.8 + * @see ResponseStatus#code() */ - public HandlerMethod getResolvedFromHandlerMethod() { - return this.resolvedFromHandlerMethod; + protected HttpStatus getResponseStatus() { + 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() { 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. *

Also supports merged composed annotations with attribute * overrides as of Spring Framework 4.2.2. @@ -248,6 +280,14 @@ public class HandlerMethod { 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, * the bean name is resolved before a {@link HandlerMethod} is created and returned. diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java index b7a6811f9d0..ff9f10c6c7b 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java @@ -31,12 +31,10 @@ import reactor.core.publisher.Mono; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; -import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.http.HttpStatus; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; -import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.method.HandlerMethod; import org.springframework.web.reactive.BindingContext; 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 HttpStatus responseStatus; - private List resolvers = new ArrayList<>(); @@ -67,23 +63,12 @@ public class InvocableHandlerMethod extends HandlerMethod { public InvocableHandlerMethod(HandlerMethod handlerMethod) { super(handlerMethod); - initResponseStatus(); } public InvocableHandlerMethod(Object bean, Method 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 @@ -132,8 +117,9 @@ public class InvocableHandlerMethod extends HandlerMethod { try { Object value = doInvoke(args); HandlerResult result = new HandlerResult(this, value, getReturnType(), bindingContext); - if (this.responseStatus != null) { - exchange.getResponse().setStatusCode(this.responseStatus); + HttpStatus status = getResponseStatus(); + if (status != null) { + exchange.getResponse().setStatusCode(status); } return Mono.just(result); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java index 46288491236..8ea68dc58a6 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletInvocableHandlerMethod.java @@ -25,7 +25,6 @@ import java.util.concurrent.Callable; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; -import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.http.HttpStatus; import org.springframework.util.ClassUtils; 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 HttpStatus responseStatus; - - private String responseReason; - private HandlerMethodReturnValueHandlerComposite returnValueHandlers; @@ -72,7 +66,6 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { */ public ServletInvocableHandlerMethod(Object handler, Method method) { super(handler, method); - initResponseStatus(); } /** @@ -80,21 +73,9 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { */ public ServletInvocableHandlerMethod(HandlerMethod 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 * handle return values. @@ -118,12 +99,12 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { setResponseStatus(webRequest); if (returnValue == null) { - if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) { + if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { mavContainer.setRequestHandled(true); return; } } - else if (StringUtils.hasText(this.responseReason)) { + else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; } @@ -145,17 +126,21 @@ public class ServletInvocableHandlerMethod extends InvocableHandlerMethod { * Set the response status according to the {@link ResponseStatus} annotation. */ private void setResponseStatus(ServletWebRequest webRequest) throws IOException { - if (this.responseStatus == null) { + HttpStatus status = getResponseStatus(); + if (status == null) { 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 { - webRequest.getResponse().setStatus(this.responseStatus.value()); + webRequest.getResponse().setStatus(status.value()); } + // 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(); } - /** - * Does this method have the response status instruction? - */ - private boolean hasResponseStatus() { - return (this.responseStatus != null); - } - private String getReturnValueHandlingErrorMessage(String message, Object returnValue) { StringBuilder sb = new StringBuilder(message); if (returnValue != null) {