Consistent method selection for listeners and endpoint mappings
Issue: SPR-13654
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Defines the algorithm for searching for metadata-associated methods exhaustively
|
||||
* including interfaces and parent classes while also dealing with parameterized methods
|
||||
* as well as common scenarios encountered with interface and class-based proxies.
|
||||
*
|
||||
* <p>Typically, but not necessarily, used for finding annotated handler methods.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.2.3
|
||||
*/
|
||||
public abstract class MethodIntrospector {
|
||||
|
||||
/**
|
||||
* Select methods on the given target type based on the lookup of associated metadata.
|
||||
* <p>Callers define methods of interest through the {@link MetadataLookup} parameter,
|
||||
* allowing to collect the associated metadata into the result map.
|
||||
* @param targetType the target type to search methods on
|
||||
* @param metadataLookup a {@link MetadataLookup} callback to inspect methods of interest,
|
||||
* returning non-null metadata to be associated with a given method if there is a match,
|
||||
* or {@code null} for no match
|
||||
* @return the selected methods associated with their metadata (in the order of retrieval),
|
||||
* or an empty map in case of no match
|
||||
*/
|
||||
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
|
||||
final Map<Method, T> methodMap = new LinkedHashMap<Method, T>();
|
||||
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
|
||||
Class<?> specificHandlerType = null;
|
||||
|
||||
if (!Proxy.isProxyClass(targetType)) {
|
||||
handlerTypes.add(targetType);
|
||||
specificHandlerType = targetType;
|
||||
}
|
||||
handlerTypes.addAll(Arrays.asList(targetType.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);
|
||||
T result = metadataLookup.inspect(specificMethod);
|
||||
if (result != null) {
|
||||
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
|
||||
methodMap.put(specificMethod, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, ReflectionUtils.USER_DECLARED_METHODS);
|
||||
}
|
||||
|
||||
return methodMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select methods on the given target type based on a filter.
|
||||
* <p>Callers define methods of interest through the
|
||||
* {@link ReflectionUtils.MethodFilter} parameter.
|
||||
* @param targetType the target type to search methods on
|
||||
* @param methodFilter a {@link ReflectionUtils.MethodFilter} to help
|
||||
* recognize handler methods of interest
|
||||
* @return the selected methods, or an empty set in case of no match
|
||||
*/
|
||||
public static Set<Method> selectMethods(Class<?> targetType, final ReflectionUtils.MethodFilter methodFilter) {
|
||||
return selectMethods(targetType, new MetadataLookup<Boolean>() {
|
||||
@Override
|
||||
public Boolean inspect(Method method) {
|
||||
return (methodFilter.matches(method) ? Boolean.TRUE : null);
|
||||
}
|
||||
}).keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Select an invocable method on the target type: either the given method itself
|
||||
* if actually exposed on the target type, or otherwise a corresponding method
|
||||
* on one of the target type's interfaces or on the target type itself.
|
||||
* <p>Matches on user-declared interfaces will be preferred since they are likely
|
||||
* to contain relevant metadata that corresponds to the method on the target class.
|
||||
* @param method the method to check
|
||||
* @param targetType the target type to search methods on
|
||||
* (typically an interface-based JDK proxy)
|
||||
* @return a corresponding invocable method on the target type
|
||||
*/
|
||||
public static Method selectInvocableMethod(Method method, Class<?> targetType) {
|
||||
if (method.getDeclaringClass().isAssignableFrom(targetType)) {
|
||||
return method;
|
||||
}
|
||||
try {
|
||||
for (Class<?> ifc : targetType.getInterfaces()) {
|
||||
try {
|
||||
return ifc.getMethod(method.getName(), method.getParameterTypes());
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
// Alright, not on this interface then...
|
||||
}
|
||||
}
|
||||
// A final desperate attempt on the proxy class itself...
|
||||
return targetType.getMethod(method.getName(), method.getParameterTypes());
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Need to invoke method '%s' declared on target class '%s', " +
|
||||
"but not found in any interface(s) of the exposed proxy type. " +
|
||||
"Either pull the method up to an interface or switch to CGLIB " +
|
||||
"proxies by enforcing proxy-target-class mode in your configuration.",
|
||||
method.getName(), method.getDeclaringClass().getSimpleName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A callback interface for metadata lookup on a given method.
|
||||
* @param <T> the type of metadata returned
|
||||
*/
|
||||
public interface MetadataLookup<T> {
|
||||
|
||||
/**
|
||||
* Perform a lookup on the given method and return associated metadata, if any.
|
||||
* @param method the method to inspect
|
||||
* @return non-null metadata to be associated with a method if there is a match,
|
||||
* or {@code null} for no match
|
||||
*/
|
||||
T inspect(Method method);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -17,14 +17,9 @@
|
||||
package org.springframework.web.method;
|
||||
|
||||
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 3.1
|
||||
* @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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -25,12 +25,12 @@ import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.core.ExceptionDepthComparator;
|
||||
import org.springframework.core.MethodIntrospector;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
|
||||
/**
|
||||
* Discovers {@linkplain ExceptionHandler @ExceptionHandler} methods in a given class,
|
||||
@@ -70,7 +70,7 @@ public class ExceptionHandlerMethodResolver {
|
||||
* @param handlerType the type to introspect
|
||||
*/
|
||||
public ExceptionHandlerMethodResolver(Class<?> handlerType) {
|
||||
for (Method method : HandlerMethodSelector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
|
||||
for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
|
||||
for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
|
||||
addExceptionMapping(exceptionType, method);
|
||||
}
|
||||
@@ -105,9 +105,8 @@ public class ExceptionHandlerMethodResolver {
|
||||
private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
|
||||
Method oldMethod = this.mappedMethods.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 + "}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.MethodIntrospector;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.test.MockHttpServletResponse;
|
||||
@@ -42,7 +43,6 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
|
||||
import org.springframework.web.method.support.InvocableHandlerMethod;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
@@ -119,7 +119,7 @@ public class ModelFactoryOrderingTests {
|
||||
WebDataBinderFactory dataBinderFactory = new DefaultDataBinderFactory(null);
|
||||
|
||||
Class<?> type = controller.getClass();
|
||||
Set<Method> methods = HandlerMethodSelector.selectMethods(type, METHOD_FILTER);
|
||||
Set<Method> methods = MethodIntrospector.selectMethods(type, METHOD_FILTER);
|
||||
List<InvocableHandlerMethod> modelMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
for (Method method : methods) {
|
||||
InvocableHandlerMethod modelMethod = new InvocableHandlerMethod(controller, method);
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -34,15 +33,14 @@ import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.MethodIntrospector;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
@@ -197,29 +195,23 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
* @param handler the bean name of a handler or a handler instance
|
||||
*/
|
||||
protected void detectHandlerMethods(final Object handler) {
|
||||
Class<?> handlerType =
|
||||
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
|
||||
|
||||
// Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
|
||||
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
|
||||
Class<?> handlerType = (handler instanceof String ?
|
||||
getApplicationContext().getType((String) handler) : handler.getClass());
|
||||
final Class<?> userType = ClassUtils.getUserClass(handlerType);
|
||||
|
||||
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
|
||||
@Override
|
||||
public boolean matches(Method method) {
|
||||
T mapping = getMappingForMethod(method, userType);
|
||||
if (mapping != null) {
|
||||
mappings.put(method, mapping);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
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) {
|
||||
registerHandlerMethod(handler, method, mappings.get(method));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
|
||||
}
|
||||
for (Map.Entry<Method, T> entry : methods.entrySet()) {
|
||||
registerHandlerMethod(handler, entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,12 +233,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
* @param mapping the mapping conditions associated with the handler method
|
||||
* @throws IllegalStateException if another method was already registered
|
||||
* under the same mapping
|
||||
* @deprecated as of 4.2. You can now invoke the public methods
|
||||
* {@link #registerMapping(Object, Object, Method)} and
|
||||
* {@link #unregisterMapping(Object)} during initialization or at runtime,
|
||||
* i.e. after initialization is complete.
|
||||
*/
|
||||
@Deprecated
|
||||
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
|
||||
this.mappingRegistry.register(mapping, handler, method);
|
||||
}
|
||||
@@ -443,7 +430,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
}
|
||||
else {
|
||||
CorsConfiguration corsConfigFromMethod = this.mappingRegistry.getCorsConfiguration(handlerMethod);
|
||||
corsConfig = (corsConfig == null ? corsConfigFromMethod : corsConfig.combine(corsConfigFromMethod));
|
||||
corsConfig = (corsConfig != null ? corsConfig.combine(corsConfigFromMethod) : corsConfigFromMethod);
|
||||
}
|
||||
}
|
||||
return corsConfig;
|
||||
@@ -470,10 +457,8 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
private final Map<HandlerMethod, CorsConfiguration> corsLookup =
|
||||
new ConcurrentHashMap<HandlerMethod, CorsConfiguration>();
|
||||
|
||||
|
||||
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||
|
||||
|
||||
/**
|
||||
* Return all mappings and handler methods. Not thread-safe.
|
||||
* @see #acquireReadLock()
|
||||
@@ -521,7 +506,6 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
|
||||
|
||||
public void register(T mapping, Object handler, Method method) {
|
||||
|
||||
this.readWriteLock.writeLock().lock();
|
||||
try {
|
||||
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
|
||||
@@ -560,8 +544,8 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
if (handlerMethod != null && !handlerMethod.equals(newHandlerMethod)) {
|
||||
throw new IllegalStateException(
|
||||
"Ambiguous mapping. Cannot map '" + newHandlerMethod.getBean() + "' method \n" +
|
||||
newHandlerMethod + "\nto " + mapping + ": There is already '" +
|
||||
handlerMethod.getBean() + "' bean method\n" + handlerMethod + " mapped.");
|
||||
newHandlerMethod + "\nto " + mapping + ": There is already '" +
|
||||
handlerMethod.getBean() + "' bean method\n" + handlerMethod + " mapped.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,9 +560,10 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
}
|
||||
|
||||
private void addMappingName(String name, HandlerMethod handlerMethod) {
|
||||
|
||||
List<HandlerMethod> oldList = this.nameLookup.containsKey(name) ?
|
||||
this.nameLookup.get(name) : Collections.<HandlerMethod>emptyList();
|
||||
List<HandlerMethod> oldList = this.nameLookup.get(name);
|
||||
if (oldList == null) {
|
||||
oldList = Collections.<HandlerMethod>emptyList();
|
||||
}
|
||||
|
||||
for (HandlerMethod current : oldList) {
|
||||
if (handlerMethod.equals(current)) {
|
||||
@@ -587,7 +572,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Mapping name=" + name);
|
||||
logger.trace("Mapping name '" + name + "'");
|
||||
}
|
||||
|
||||
List<HandlerMethod> newList = new ArrayList<HandlerMethod>(oldList.size() + 1);
|
||||
@@ -597,7 +582,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
|
||||
if (newList.size() > 1) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Mapping name clash for handlerMethods=" + newList +
|
||||
logger.trace("Mapping name clash for handlerMethods " + newList +
|
||||
". Consider assigning explicit names.");
|
||||
}
|
||||
}
|
||||
@@ -654,9 +639,9 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
}
|
||||
this.nameLookup.put(name, newList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class MappingRegistration<T> {
|
||||
|
||||
private final T mapping;
|
||||
@@ -667,20 +652,15 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||
|
||||
private final String mappingName;
|
||||
|
||||
|
||||
public MappingRegistration(T mapping, HandlerMethod handlerMethod,
|
||||
List<String> directUrls, String mappingName) {
|
||||
|
||||
public MappingRegistration(T mapping, HandlerMethod handlerMethod, List<String> directUrls, String mappingName) {
|
||||
Assert.notNull(mapping);
|
||||
Assert.notNull(handlerMethod);
|
||||
|
||||
this.mapping = mapping;
|
||||
this.handlerMethod = handlerMethod;
|
||||
this.directUrls = (directUrls != null ? directUrls : Collections.<String>emptyList());
|
||||
this.mappingName = mappingName;
|
||||
}
|
||||
|
||||
|
||||
public T getMapping() {
|
||||
return this.mapping;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.springframework.cglib.proxy.Factory;
|
||||
import org.springframework.cglib.proxy.MethodProxy;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.MethodIntrospector;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.SynthesizingMethodParameter;
|
||||
@@ -57,7 +58,6 @@ import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.CompositeUriComponentsContributor;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
@@ -454,7 +454,7 @@ public class MvcUriComponentsBuilder {
|
||||
return (name.equals(methodName) && argLength == args.length);
|
||||
}
|
||||
};
|
||||
Set<Method> methods = HandlerMethodSelector.selectMethods(controllerType, selector);
|
||||
Set<Method> methods = MethodIntrospector.selectMethods(controllerType, selector);
|
||||
if (methods.size() == 1) {
|
||||
return methods.iterator().next();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodIntrospector;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
@@ -70,7 +71,6 @@ import org.springframework.web.context.request.async.WebAsyncTask;
|
||||
import org.springframework.web.context.request.async.WebAsyncUtils;
|
||||
import org.springframework.web.method.ControllerAdviceBean;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
import org.springframework.web.method.annotation.ErrorsMethodArgumentResolver;
|
||||
import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver;
|
||||
import org.springframework.web.method.annotation.InitBinderDataBinderFactory;
|
||||
@@ -538,23 +538,31 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
||||
List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>();
|
||||
|
||||
for (ControllerAdviceBean bean : beans) {
|
||||
Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
|
||||
Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
|
||||
if (!attrMethods.isEmpty()) {
|
||||
this.modelAttributeAdviceCache.put(bean, attrMethods);
|
||||
logger.info("Detected @ModelAttribute methods in " + bean);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Detected @ModelAttribute methods in " + bean);
|
||||
}
|
||||
}
|
||||
Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
|
||||
Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
|
||||
if (!binderMethods.isEmpty()) {
|
||||
this.initBinderAdviceCache.put(bean, binderMethods);
|
||||
logger.info("Detected @InitBinder methods in " + bean);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Detected @InitBinder methods in " + bean);
|
||||
}
|
||||
}
|
||||
if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
|
||||
requestResponseBodyAdviceBeans.add(bean);
|
||||
logger.info("Detected RequestBodyAdvice bean in " + bean);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Detected RequestBodyAdvice bean in " + bean);
|
||||
}
|
||||
}
|
||||
if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
|
||||
requestResponseBodyAdviceBeans.add(bean);
|
||||
logger.info("Detected ResponseBodyAdvice bean in " + bean);
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Detected ResponseBodyAdvice bean in " + bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -826,7 +834,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
Set<Method> methods = this.modelAttributeCache.get(handlerType);
|
||||
if (methods == null) {
|
||||
methods = HandlerMethodSelector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
|
||||
methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
|
||||
this.modelAttributeCache.put(handlerType, methods);
|
||||
}
|
||||
List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
@@ -858,7 +866,7 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
||||
Class<?> handlerType = handlerMethod.getBeanType();
|
||||
Set<Method> methods = this.initBinderCache.get(handlerType);
|
||||
if (methods == null) {
|
||||
methods = HandlerMethodSelector.selectMethods(handlerType, INIT_BINDER_METHODS);
|
||||
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
|
||||
this.initBinderCache.put(handlerType, methods);
|
||||
}
|
||||
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
|
||||
@@ -925,7 +933,6 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
||||
* MethodFilter that matches {@link InitBinder @InitBinder} methods.
|
||||
*/
|
||||
public static final MethodFilter INIT_BINDER_METHODS = new MethodFilter() {
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method) {
|
||||
return AnnotationUtils.findAnnotation(method, InitBinder.class) != null;
|
||||
@@ -936,7 +943,6 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
||||
* MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods.
|
||||
*/
|
||||
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() {
|
||||
|
||||
@Override
|
||||
public boolean matches(Method method) {
|
||||
return ((AnnotationUtils.findAnnotation(method, RequestMapping.class) == null) &&
|
||||
|
||||
Reference in New Issue
Block a user