From 24034447f60ac62caf2f1683eeff7ed88aa89740 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 1 Mar 2017 15:45:50 -0500 Subject: [PATCH] Polish ExceptionHandler initialization In preparation for SPR-15132 --- .../RequestMappingHandlerAdapter.java | 71 +++++++++---------- .../annotation/ModelInitializerTests.java | 8 +-- 2 files changed, 38 insertions(+), 41 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java index 28f8c984bbc..d8bac642ff6 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerAdapter.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import java.util.stream.Collectors; import org.apache.commons.logging.Log; @@ -88,9 +89,9 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory private ModelInitializer modelInitializer; - private final Map, Set> initBinderCache = new ConcurrentHashMap<>(64); + private final Map, Set> binderMethodCache = new ConcurrentHashMap<>(64); - private final Map, Set> modelAttributeCache = new ConcurrentHashMap<>(64); + private final Map, Set> attributeMethodCache = new ConcurrentHashMap<>(64); private final Map, ExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap<>(64); @@ -305,29 +306,33 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory public Mono handle(ServerWebExchange exchange, Object handler) { HandlerMethod handlerMethod = (HandlerMethod) handler; - InvocableHandlerMethod invocable = new InvocableHandlerMethod(handlerMethod); - invocable.setArgumentResolvers(getArgumentResolvers()); BindingContext bindingContext = new InitBinderBindingContext( - getWebBindingInitializer(), getInitBinderMethods(handlerMethod)); + getWebBindingInitializer(), getBinderMethods(handlerMethod)); Mono modelCompletion = this.modelInitializer.initModel( bindingContext, getAttributeMethods(handlerMethod), exchange); - return modelCompletion.then(() -> - invocable.invoke(exchange, bindingContext) - .doOnNext(result -> result.setExceptionHandler( - ex -> handleException(ex, handlerMethod, bindingContext, exchange))) - .otherwise(ex -> handleException( - ex, handlerMethod, bindingContext, exchange))); + Function> exceptionHandler = + ex -> handleException(ex, handlerMethod, bindingContext, exchange); + + return modelCompletion.then(() -> { + + InvocableHandlerMethod invocable = new InvocableHandlerMethod(handlerMethod); + invocable.setArgumentResolvers(getArgumentResolvers()); + + return invocable.invoke(exchange, bindingContext) + .doOnNext(result -> result.setExceptionHandler(exceptionHandler)) + .otherwise(exceptionHandler); + }); } - private List getInitBinderMethods(HandlerMethod handlerMethod) { + private List getBinderMethods(HandlerMethod handlerMethod) { Class handlerType = handlerMethod.getBeanType(); - Set methods = this.initBinderCache.computeIfAbsent(handlerType, aClass -> - MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS)); + Set methods = this.binderMethodCache.computeIfAbsent(handlerType, aClass -> + MethodIntrospector.selectMethods(handlerType, BINDER_METHODS)); return methods.stream() .map(method -> { @@ -343,8 +348,8 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory Class handlerType = handlerMethod.getBeanType(); - Set methods = this.modelAttributeCache.computeIfAbsent(handlerType, aClass -> - MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS)); + Set methods = this.attributeMethodCache.computeIfAbsent(handlerType, aClass -> + MethodIntrospector.selectMethods(handlerType, ATTRIBUTE_METHODS)); return methods.stream() .map(method -> { @@ -359,51 +364,43 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory private Mono handleException(Throwable ex, HandlerMethod handlerMethod, BindingContext bindingContext, ServerWebExchange exchange) { - InvocableHandlerMethod invocable = findExceptionHandler(handlerMethod, ex); - if (invocable != null) { + ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache + .computeIfAbsent(handlerMethod.getBeanType(), ExceptionHandlerMethodResolver::new); + + Method method = resolver.resolveMethodByExceptionType(ex.getClass()); + + if (method != null) { + Object bean = handlerMethod.getBean(); + InvocableHandlerMethod invocable = new InvocableHandlerMethod(bean, method); + invocable.setArgumentResolvers(getArgumentResolvers()); try { if (logger.isDebugEnabled()) { logger.debug("Invoking @ExceptionHandler method: " + invocable.getMethod()); } - invocable.setArgumentResolvers(getArgumentResolvers()); bindingContext.getModel().asMap().clear(); return invocable.invoke(exchange, bindingContext, ex); } catch (Throwable invocationEx) { if (logger.isWarnEnabled()) { - logger.warn("Failed to invoke @ExceptionHandler method: " + invocable.getMethod(), - invocationEx); + logger.warn("Failed to invoke: " + invocable.getMethod(), invocationEx); } } } - return Mono.error(ex); - } - protected InvocableHandlerMethod findExceptionHandler(HandlerMethod handlerMethod, Throwable exception) { - if (handlerMethod == null) { - return null; - } - Class handlerType = handlerMethod.getBeanType(); - ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType); - if (resolver == null) { - resolver = new ExceptionHandlerMethodResolver(handlerType); - this.exceptionHandlerCache.put(handlerType, resolver); - } - Method method = resolver.resolveMethodByExceptionType(exception.getClass()); - return (method != null ? new InvocableHandlerMethod(handlerMethod.getBean(), method) : null); + return Mono.error(ex); } /** * MethodFilter that matches {@link InitBinder @InitBinder} methods. */ - public static final ReflectionUtils.MethodFilter INIT_BINDER_METHODS = method -> + public static final ReflectionUtils.MethodFilter BINDER_METHODS = method -> AnnotationUtils.findAnnotation(method, InitBinder.class) != null; /** * MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods. */ - public static final ReflectionUtils.MethodFilter MODEL_ATTRIBUTE_METHODS = method -> + public static final ReflectionUtils.MethodFilter ATTRIBUTE_METHODS = method -> (AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) && (AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelInitializerTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelInitializerTests.java index 556658d5b35..4b10e248d54 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelInitializerTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ModelInitializerTests.java @@ -48,8 +48,8 @@ import org.springframework.web.server.adapter.DefaultServerWebExchange; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; -import static org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter.INIT_BINDER_METHODS; -import static org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter.MODEL_ATTRIBUTE_METHODS; +import static org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter.BINDER_METHODS; +import static org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter.ATTRIBUTE_METHODS; /** * Unit tests for {@link ModelInitializer}. @@ -111,14 +111,14 @@ public class ModelInitializerTests { private List getBinderMethods(Object controller) { return MethodIntrospector - .selectMethods(controller.getClass(), INIT_BINDER_METHODS).stream() + .selectMethods(controller.getClass(), BINDER_METHODS).stream() .map(method -> new SyncInvocableHandlerMethod(controller, method)) .collect(Collectors.toList()); } private List getAttributeMethods(Object controller) { return MethodIntrospector - .selectMethods(controller.getClass(), MODEL_ATTRIBUTE_METHODS).stream() + .selectMethods(controller.getClass(), ATTRIBUTE_METHODS).stream() .map(method -> { InvocableHandlerMethod invocable = new InvocableHandlerMethod(controller, method); invocable.setArgumentResolvers(Collections.singletonList(new ModelArgumentResolver()));