Consistent method selection for listeners and endpoint mappings

Issue: SPR-13654
This commit is contained in:
Juergen Hoeller
2015-11-09 14:57:45 +01:00
parent 5b06150096
commit bc7bcab578
10 changed files with 264 additions and 160 deletions

View File

@@ -17,14 +17,9 @@
package org.springframework.messaging.handler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.core.MethodIntrospector;
import org.springframework.util.ReflectionUtils.MethodFilter;
/**
@@ -33,7 +28,9 @@ import org.springframework.util.ReflectionUtils.MethodFilter;
*
* @author Rossen Stoyanchev
* @since 4.0
* @deprecated as of Spring 4.2.3, in favor of the generalized and refined {@link MethodIntrospector}
*/
@Deprecated
public abstract class HandlerMethodSelector {
/**
@@ -42,31 +39,10 @@ public abstract class HandlerMethodSelector {
* @param handlerType the handler type to search handler methods on
* @param handlerMethodFilter a {@link MethodFilter} to help recognize handler methods of interest
* @return the selected methods, or an empty set
* @see MethodIntrospector#selectMethods(Class, MethodFilter)
*/
public static Set<Method> selectMethods(final Class<?> handlerType, final MethodFilter handlerMethodFilter) {
final Set<Method> handlerMethods = new LinkedHashSet<Method>();
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
Class<?> specificHandlerType = null;
if (!Proxy.isProxyClass(handlerType)) {
handlerTypes.add(handlerType);
specificHandlerType = handlerType;
}
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (handlerMethodFilter.matches(specificMethod) &&
(bridgedMethod == specificMethod || !handlerMethodFilter.matches(bridgedMethod))) {
handlerMethods.add(specificMethod);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return handlerMethods;
public static Set<Method> selectMethods(Class<?> handlerType, MethodFilter handlerMethodFilter) {
return MethodIntrospector.selectMethods(handlerType, handlerMethodFilter);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.handler.HandlerMethodSelector;
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
import org.springframework.messaging.handler.invocation.AbstractExceptionHandlerMethodResolver;
import org.springframework.util.ReflectionUtils.MethodFilter;
@@ -49,32 +49,39 @@ public class AnnotationExceptionHandlerMethodResolver extends AbstractExceptionH
}
private static Map<Class<? extends Throwable>, Method> initExceptionMappings(Class<?> handlerType) {
Map<Method, MessageExceptionHandler> methods = MethodIntrospector.selectMethods(handlerType,
new MethodIntrospector.MetadataLookup<MessageExceptionHandler>() {
@Override
public MessageExceptionHandler inspect(Method method) {
return AnnotationUtils.findAnnotation(method, MessageExceptionHandler.class);
}
});
Map<Class<? extends Throwable>, Method> result = new HashMap<Class<? extends Throwable>, Method>();
for (Method method : HandlerMethodSelector.selectMethods(handlerType, EXCEPTION_HANDLER_METHOD_FILTER)) {
for (Class<? extends Throwable> exceptionType : getMappedExceptions(method)) {
for (Map.Entry<Method, MessageExceptionHandler> entry : methods.entrySet()) {
Method method = entry.getKey();
List<Class<? extends Throwable>> exceptionTypes = new ArrayList<Class<? extends Throwable>>();
exceptionTypes.addAll(Arrays.asList(entry.getValue().value()));
if (exceptionTypes.isEmpty()) {
exceptionTypes.addAll(getExceptionsFromMethodSignature(method));
}
for (Class<? extends Throwable> exceptionType : exceptionTypes) {
Method oldMethod = result.put(exceptionType, method);
if (oldMethod != null && !oldMethod.equals(method)) {
throw new IllegalStateException(
"Ambiguous @ExceptionHandler method mapped for [" + exceptionType + "]: {" +
oldMethod + ", " + method + "}.");
throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +
exceptionType + "]: {" + oldMethod + ", " + method + "}");
}
}
}
return result;
}
private static List<Class<? extends Throwable>> getMappedExceptions(Method method) {
List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>();
MessageExceptionHandler annot = AnnotationUtils.findAnnotation(method, MessageExceptionHandler.class);
result.addAll(Arrays.asList(annot.value()));
if (result.isEmpty()) {
result.addAll(getExceptionsFromMethodSignature(method));
}
return result;
}
/** A filter for selecting annotated exception handling methods. */
/**
* A filter for selecting annotated exception handling methods.
* @deprecated as of Spring 4.2.3, since it isn't used anymore
*/
@Deprecated
public final static MethodFilter EXCEPTION_HANDLER_METHOD_FILTER = new MethodFilter() {
@Override

View File

@@ -34,20 +34,20 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodParameter;
import org.springframework.core.MethodIntrospector;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.handler.DestinationPatternsMessageCondition;
import org.springframework.messaging.handler.HandlerMethod;
import org.springframework.messaging.handler.HandlerMethodSelector;
import org.springframework.messaging.handler.MessagingAdviceBean;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
@@ -60,6 +60,7 @@ import org.springframework.util.concurrent.ListenableFutureCallback;
* exceptions raised during message handling.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 4.0
* @param <T> the type of the Object that contains information mapping a
* {@link org.springframework.messaging.handler.HandlerMethod} to incoming messages
@@ -250,22 +251,24 @@ public abstract class AbstractMethodMessageHandler<T>
* so register it with the extracted mapping information.
* @param handler the handler to check, either an instance of a Spring bean name
*/
protected final void detectHandlerMethods(Object handler) {
protected final void detectHandlerMethods(final Object handler) {
Class<?> handlerType = (handler instanceof String ?
this.applicationContext.getType((String) handler) : handler.getClass());
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new ReflectionUtils.MethodFilter() {
@Override
public boolean matches(Method method) {
return getMappingForMethod(method, userType) != null;
}
});
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method method) {
return getMappingForMethod(method, userType);
}
});
for (Method method : methods) {
T mapping = getMappingForMethod(method, userType);
registerHandlerMethod(handler, method, mapping);
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " message handler methods found on " + userType + ": " + methods);
}
for (Map.Entry<Method, T> entry : methods.entrySet()) {
registerHandlerMethod(handler, entry.getKey(), entry.getValue());
}
}
@@ -277,7 +280,6 @@ public abstract class AbstractMethodMessageHandler<T>
*/
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
/**
* Register a handler method and its unique mapping.
* @param handler the bean name of the handler or the handler instance
@@ -287,6 +289,7 @@ public abstract class AbstractMethodMessageHandler<T>
* under the same mapping
*/
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
Assert.notNull(mapping, "Mapping must bot be null");
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);