Polish ExceptionHandler initialization
In preparation for SPR-15132
This commit is contained in:
parent
d8b150e83d
commit
24034447f6
|
|
@ -22,6 +22,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
|
|
@ -88,9 +89,9 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
||||||
|
|
||||||
private ModelInitializer modelInitializer;
|
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 =
|
private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =
|
||||||
new ConcurrentHashMap<>(64);
|
new ConcurrentHashMap<>(64);
|
||||||
|
|
@ -305,29 +306,33 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
||||||
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
|
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
|
||||||
|
|
||||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||||
InvocableHandlerMethod invocable = new InvocableHandlerMethod(handlerMethod);
|
|
||||||
invocable.setArgumentResolvers(getArgumentResolvers());
|
|
||||||
|
|
||||||
BindingContext bindingContext = new InitBinderBindingContext(
|
BindingContext bindingContext = new InitBinderBindingContext(
|
||||||
getWebBindingInitializer(), getInitBinderMethods(handlerMethod));
|
getWebBindingInitializer(), getBinderMethods(handlerMethod));
|
||||||
|
|
||||||
Mono<Void> modelCompletion = this.modelInitializer.initModel(
|
Mono<Void> modelCompletion = this.modelInitializer.initModel(
|
||||||
bindingContext, getAttributeMethods(handlerMethod), exchange);
|
bindingContext, getAttributeMethods(handlerMethod), exchange);
|
||||||
|
|
||||||
return modelCompletion.then(() ->
|
Function<Throwable, Mono<HandlerResult>> exceptionHandler =
|
||||||
invocable.invoke(exchange, bindingContext)
|
ex -> handleException(ex, handlerMethod, bindingContext, exchange);
|
||||||
.doOnNext(result -> result.setExceptionHandler(
|
|
||||||
ex -> handleException(ex, handlerMethod, bindingContext, exchange)))
|
return modelCompletion.then(() -> {
|
||||||
.otherwise(ex -> handleException(
|
|
||||||
ex, handlerMethod, bindingContext, exchange)));
|
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();
|
Class<?> handlerType = handlerMethod.getBeanType();
|
||||||
|
|
||||||
Set<Method> methods = this.initBinderCache.computeIfAbsent(handlerType, aClass ->
|
Set<Method> methods = this.binderMethodCache.computeIfAbsent(handlerType, aClass ->
|
||||||
MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS));
|
MethodIntrospector.selectMethods(handlerType, BINDER_METHODS));
|
||||||
|
|
||||||
return methods.stream()
|
return methods.stream()
|
||||||
.map(method -> {
|
.map(method -> {
|
||||||
|
|
@ -343,8 +348,8 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
||||||
|
|
||||||
Class<?> handlerType = handlerMethod.getBeanType();
|
Class<?> handlerType = handlerMethod.getBeanType();
|
||||||
|
|
||||||
Set<Method> methods = this.modelAttributeCache.computeIfAbsent(handlerType, aClass ->
|
Set<Method> methods = this.attributeMethodCache.computeIfAbsent(handlerType, aClass ->
|
||||||
MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS));
|
MethodIntrospector.selectMethods(handlerType, ATTRIBUTE_METHODS));
|
||||||
|
|
||||||
return methods.stream()
|
return methods.stream()
|
||||||
.map(method -> {
|
.map(method -> {
|
||||||
|
|
@ -359,51 +364,43 @@ public class RequestMappingHandlerAdapter implements HandlerAdapter, BeanFactory
|
||||||
private Mono<HandlerResult> handleException(Throwable ex, HandlerMethod handlerMethod,
|
private Mono<HandlerResult> handleException(Throwable ex, HandlerMethod handlerMethod,
|
||||||
BindingContext bindingContext, ServerWebExchange exchange) {
|
BindingContext bindingContext, ServerWebExchange exchange) {
|
||||||
|
|
||||||
InvocableHandlerMethod invocable = findExceptionHandler(handlerMethod, ex);
|
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache
|
||||||
if (invocable != null) {
|
.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 {
|
try {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Invoking @ExceptionHandler method: " + invocable.getMethod());
|
logger.debug("Invoking @ExceptionHandler method: " + invocable.getMethod());
|
||||||
}
|
}
|
||||||
invocable.setArgumentResolvers(getArgumentResolvers());
|
|
||||||
bindingContext.getModel().asMap().clear();
|
bindingContext.getModel().asMap().clear();
|
||||||
return invocable.invoke(exchange, bindingContext, ex);
|
return invocable.invoke(exchange, bindingContext, ex);
|
||||||
}
|
}
|
||||||
catch (Throwable invocationEx) {
|
catch (Throwable invocationEx) {
|
||||||
if (logger.isWarnEnabled()) {
|
if (logger.isWarnEnabled()) {
|
||||||
logger.warn("Failed to invoke @ExceptionHandler method: " + invocable.getMethod(),
|
logger.warn("Failed to invoke: " + invocable.getMethod(), invocationEx);
|
||||||
invocationEx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Mono.error(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected InvocableHandlerMethod findExceptionHandler(HandlerMethod handlerMethod, Throwable exception) {
|
return Mono.error(ex);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MethodFilter that matches {@link InitBinder @InitBinder} methods.
|
* 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;
|
AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods.
|
* 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, RequestMapping.class) == null) &&
|
||||||
(AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null);
|
(AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ import org.springframework.web.server.adapter.DefaultServerWebExchange;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.mockito.Mockito.mock;
|
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.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.ATTRIBUTE_METHODS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link ModelInitializer}.
|
* Unit tests for {@link ModelInitializer}.
|
||||||
|
|
@ -111,14 +111,14 @@ public class ModelInitializerTests {
|
||||||
|
|
||||||
private List<SyncInvocableHandlerMethod> getBinderMethods(Object controller) {
|
private List<SyncInvocableHandlerMethod> getBinderMethods(Object controller) {
|
||||||
return MethodIntrospector
|
return MethodIntrospector
|
||||||
.selectMethods(controller.getClass(), INIT_BINDER_METHODS).stream()
|
.selectMethods(controller.getClass(), BINDER_METHODS).stream()
|
||||||
.map(method -> new SyncInvocableHandlerMethod(controller, method))
|
.map(method -> new SyncInvocableHandlerMethod(controller, method))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<InvocableHandlerMethod> getAttributeMethods(Object controller) {
|
private List<InvocableHandlerMethod> getAttributeMethods(Object controller) {
|
||||||
return MethodIntrospector
|
return MethodIntrospector
|
||||||
.selectMethods(controller.getClass(), MODEL_ATTRIBUTE_METHODS).stream()
|
.selectMethods(controller.getClass(), ATTRIBUTE_METHODS).stream()
|
||||||
.map(method -> {
|
.map(method -> {
|
||||||
InvocableHandlerMethod invocable = new InvocableHandlerMethod(controller, method);
|
InvocableHandlerMethod invocable = new InvocableHandlerMethod(controller, method);
|
||||||
invocable.setArgumentResolvers(Collections.singletonList(new ModelArgumentResolver()));
|
invocable.setArgumentResolvers(Collections.singletonList(new ModelArgumentResolver()));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue