Polish ExceptionHandler initialization

In preparation for SPR-15132
This commit is contained in:
Rossen Stoyanchev 2017-03-01 15:45:50 -05:00
parent d8b150e83d
commit 24034447f6
2 changed files with 38 additions and 41 deletions

View File

@ -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<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(64);
private final Map<Class<?>, Set<Method>> binderMethodCache = new ConcurrentHashMap<>(64);
private final Map<Class<?>, Set<Method>> modelAttributeCache = new ConcurrentHashMap<>(64);
private final Map<Class<?>, Set<Method>> attributeMethodCache = new ConcurrentHashMap<>(64);
private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =
new ConcurrentHashMap<>(64);
@ -305,29 +306,33 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
public Mono<HandlerResult> 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<Void> 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<Throwable, Mono<HandlerResult>> 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<SyncInvocableHandlerMethod> getInitBinderMethods(HandlerMethod handlerMethod) {
private List<SyncInvocableHandlerMethod> getBinderMethods(HandlerMethod handlerMethod) {
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.initBinderCache.computeIfAbsent(handlerType, aClass ->
MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS));
Set<Method> 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<Method> methods = this.modelAttributeCache.computeIfAbsent(handlerType, aClass ->
MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS));
Set<Method> 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<HandlerResult> 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);

View File

@ -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<SyncInvocableHandlerMethod> 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<InvocableHandlerMethod> 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()));