Refine HandlerMethod registration to allow detection by handler instance as well as by bean name
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4276 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
parent
11c31085fe
commit
da3ad5623b
|
|
@ -96,6 +96,14 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||||
return urlPathHelper;
|
return urlPathHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the map with all {@link HandlerMethod}s. The key of the map is the generic type
|
||||||
|
* <strong>{@code <T>}</strong> containing request mapping conditions.
|
||||||
|
*/
|
||||||
|
public Map<T, HandlerMethod> getHandlerMethods() {
|
||||||
|
return Collections.unmodifiableMap(handlerMethods);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls the initialization of the superclass and detects handlers.
|
* Calls the initialization of the superclass and detects handlers.
|
||||||
*/
|
*/
|
||||||
|
|
@ -115,35 +123,36 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||||
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
|
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
|
||||||
}
|
}
|
||||||
for (String beanName : getApplicationContext().getBeanNamesForType(Object.class)) {
|
for (String beanName : getApplicationContext().getBeanNamesForType(Object.class)) {
|
||||||
if (isHandler(beanName)){
|
if (isHandler(getApplicationContext().getType(beanName))){
|
||||||
detectHandlerMethods(beanName);
|
detectHandlerMethods(beanName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if the given bean is a handler that should be introspected for handler methods.
|
* Determines if the given type could contain handler methods.
|
||||||
* @param beanName the name of the bean to check
|
* @param beanType the type to check
|
||||||
* @return true if the bean is a handler and may contain handler methods, false otherwise.
|
* @return true if this a type that could contain handler methods, false otherwise.
|
||||||
*/
|
*/
|
||||||
protected abstract boolean isHandler(String beanName);
|
protected abstract boolean isHandler(Class<?> beanType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detect and register handler methods for the specified handler.
|
* Detect and register handler methods for the specified handler.
|
||||||
|
* @param handler the bean name of a handler or a handler instance
|
||||||
*/
|
*/
|
||||||
private void detectHandlerMethods(final String beanName) {
|
protected void detectHandlerMethods(final Object handler) {
|
||||||
Class<?> handlerType = getApplicationContext().getType(beanName);
|
final Class<?> handlerType = (handler instanceof String) ?
|
||||||
|
getApplicationContext().getType((String) handler) : handler.getClass();
|
||||||
|
|
||||||
Set<Method> methods = HandlerMethodSelector.selectMethods(handlerType, new MethodFilter() {
|
Set<Method> methods = HandlerMethodSelector.selectMethods(handlerType, new MethodFilter() {
|
||||||
public boolean matches(Method method) {
|
public boolean matches(Method method) {
|
||||||
return getMappingForMethod(beanName, method) != null;
|
return getMappingForMethod(method, handlerType) != null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (Method method : methods) {
|
for (Method method : methods) {
|
||||||
HandlerMethod handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method);
|
T mapping = getMappingForMethod(method, handlerType);
|
||||||
T mapping = getMappingForMethod(beanName, method);
|
registerHandlerMethod(handler, method, mapping);
|
||||||
Set<String> paths = getMappingPaths(mapping);
|
|
||||||
registerHandlerMethod(paths, mapping, handlerMethod);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,40 +160,50 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
|
||||||
* Provides a request mapping for the given bean method. A method for which no request mapping can be determined
|
* Provides a request mapping for the given bean method. A method for which no request mapping can be determined
|
||||||
* is not considered a handler method.
|
* is not considered a handler method.
|
||||||
*
|
*
|
||||||
* @param beanName the name of the bean the method belongs to
|
|
||||||
* @param method the method to create a mapping for
|
* @param method the method to create a mapping for
|
||||||
|
* @param handlerType the actual handler type (possibly a subtype of {@code method.getDeclaringClass()})
|
||||||
* @return the mapping, or {@code null} if the method is not mapped
|
* @return the mapping, or {@code null} if the method is not mapped
|
||||||
*/
|
*/
|
||||||
protected abstract T getMappingForMethod(String beanName, Method method);
|
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a {@link HandlerMethod} with the given mapping.
|
* Registers a {@link HandlerMethod} with the given mapping.
|
||||||
*
|
*
|
||||||
* @param paths URL paths mapped to this method
|
* @param handler the bean name of the handler or the actual handler instance
|
||||||
* @param mapping the mapping for the method
|
* @param method the method to register
|
||||||
* @param handlerMethod the handler method to register
|
* @param mapping the mapping conditions associated with the handler method
|
||||||
* @throws IllegalStateException if another method was already register under the same mapping
|
* @throws IllegalStateException if another method was already register under the same mapping
|
||||||
*/
|
*/
|
||||||
protected void registerHandlerMethod(Set<String> paths, T mapping, HandlerMethod handlerMethod) {
|
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
|
||||||
Assert.notNull(mapping, "'mapping' must not be null");
|
HandlerMethod handlerMethod;
|
||||||
Assert.notNull(handlerMethod, "'handlerMethod' must not be null");
|
if (handler instanceof String) {
|
||||||
HandlerMethod mappedHandlerMethod = handlerMethods.get(mapping);
|
String beanName = (String) handler;
|
||||||
if (mappedHandlerMethod != null && !mappedHandlerMethod.equals(handlerMethod)) {
|
handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
handlerMethod = new HandlerMethod(handler, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);
|
||||||
|
if (oldHandlerMethod != null && !oldHandlerMethod.equals(handlerMethod)) {
|
||||||
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + handlerMethod.getBean()
|
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + handlerMethod.getBean()
|
||||||
+ "' bean method \n" + handlerMethod + "\nto " + mapping + ": There is already '"
|
+ "' bean method \n" + handlerMethod + "\nto " + mapping + ": There is already '"
|
||||||
+ mappedHandlerMethod.getBean() + "' bean method\n" + mappedHandlerMethod + " mapped.");
|
+ oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
|
||||||
}
|
}
|
||||||
|
|
||||||
handlerMethods.put(mapping, handlerMethod);
|
handlerMethods.put(mapping, handlerMethod);
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
|
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<String> paths = getMappingPaths(mapping);
|
||||||
for (String path : paths) {
|
for (String path : paths) {
|
||||||
urlMap.add(path, mapping);
|
urlMap.add(path, mapping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the URL paths for the given mapping.
|
* Get the URL paths associated with the given mapping.
|
||||||
*/
|
*/
|
||||||
protected abstract Set<String> getMappingPaths(T mapping);
|
protected abstract Set<String> getMappingPaths(T mapping);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ import org.springframework.web.servlet.mvc.method.condition.RequestConditionFact
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1.0
|
||||||
*/
|
*/
|
||||||
public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
|
public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
|
||||||
|
|
||||||
|
|
@ -87,13 +87,11 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
* The handler determination is made based on the presence of a type-level {@link Controller} or
|
* The handler determination in this method is made based on the presence of a type-level {@link Controller} annotation.
|
||||||
* a type-level {@link RequestMapping} annotation.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected boolean isHandler(String beanName) {
|
protected boolean isHandler(Class<?> beanType) {
|
||||||
return ((getApplicationContext().findAnnotationOnBean(beanName, RequestMapping.class) != null) ||
|
return AnnotationUtils.findAnnotation(beanType, Controller.class) != null;
|
||||||
(getApplicationContext().findAnnotationOnBean(beanName, Controller.class) != null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -102,17 +100,17 @@ public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping<R
|
||||||
* Type-level {@link RequestMapping @RequestMapping} annotations are also detected and their
|
* Type-level {@link RequestMapping @RequestMapping} annotations are also detected and their
|
||||||
* attributes combined with method-level {@link RequestMapping @RequestMapping} attributes.
|
* attributes combined with method-level {@link RequestMapping @RequestMapping} attributes.
|
||||||
*
|
*
|
||||||
* @param beanName the name of the bean the method belongs to
|
|
||||||
* @param method the method to create a mapping for
|
* @param method the method to create a mapping for
|
||||||
|
* @param handlerType the actual handler type, possibly a sub-type of {@code method.getDeclaringClass()}
|
||||||
* @return the mapping, or {@code null}
|
* @return the mapping, or {@code null}
|
||||||
* @see RequestMappingInfo#combine(RequestMappingInfo, PathMatcher)
|
* @see RequestMappingInfo#combine(RequestMappingInfo, PathMatcher)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected RequestMappingInfo getMappingForMethod(String beanName, Method method) {
|
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
|
||||||
RequestMapping annotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
|
RequestMapping annotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
RequestMappingInfo methodMapping = createFromRequestMapping(annotation);
|
RequestMappingInfo methodMapping = createFromRequestMapping(annotation);
|
||||||
RequestMapping typeAnnot = getApplicationContext().findAnnotationOnBean(beanName, RequestMapping.class);
|
RequestMapping typeAnnot = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
|
||||||
if (typeAnnot != null) {
|
if (typeAnnot != null) {
|
||||||
RequestMappingInfo typeMapping = createFromRequestMapping(typeAnnot);
|
RequestMappingInfo typeMapping = createFromRequestMapping(typeAnnot);
|
||||||
return typeMapping.combine(methodMapping, pathMatcher);
|
return typeMapping.combine(methodMapping, pathMatcher);
|
||||||
|
|
|
||||||
|
|
@ -41,46 +41,48 @@ public class HandlerMethodMappingTests {
|
||||||
|
|
||||||
private AbstractHandlerMethodMapping<String> mapping;
|
private AbstractHandlerMethodMapping<String> mapping;
|
||||||
|
|
||||||
private HandlerMethod handlerMethod1;
|
private MyHandler handler;
|
||||||
|
|
||||||
private HandlerMethod handlerMethod2;
|
private Method method1;
|
||||||
|
|
||||||
|
private Method method2;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
mapping = new MyHandlerMethodMapping();
|
mapping = new MyHandlerMethodMapping();
|
||||||
MyHandler handler = new MyHandler();
|
handler = new MyHandler();
|
||||||
handlerMethod1 = new HandlerMethod(handler, "handlerMethod1");
|
method1 = handler.getClass().getMethod("handlerMethod1");
|
||||||
handlerMethod2 = new HandlerMethod(handler, "handlerMethod2");
|
method2 = handler.getClass().getMethod("handlerMethod2");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalStateException.class)
|
@Test(expected = IllegalStateException.class)
|
||||||
public void registerDuplicates() {
|
public void registerDuplicates() {
|
||||||
mapping.registerHandlerMethod(new HashSet<String>(), "foo", handlerMethod1);
|
mapping.registerHandlerMethod(handler, method1, "foo");
|
||||||
mapping.registerHandlerMethod(new HashSet<String>(), "foo", handlerMethod2);
|
mapping.registerHandlerMethod(handler, method2, "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void directMatch() throws Exception {
|
public void directMatch() throws Exception {
|
||||||
String key = "foo";
|
String key = "foo";
|
||||||
mapping.registerHandlerMethod(new HashSet<String>(), key, handlerMethod1);
|
mapping.registerHandlerMethod(handler, method1, key);
|
||||||
|
|
||||||
HandlerMethod result = mapping.getHandlerInternal(new MockHttpServletRequest("GET", key));
|
HandlerMethod result = mapping.getHandlerInternal(new MockHttpServletRequest("GET", key));
|
||||||
assertEquals(handlerMethod1, result);
|
assertEquals(method1, result.getMethod());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void patternMatch() throws Exception {
|
public void patternMatch() throws Exception {
|
||||||
mapping.registerHandlerMethod(new HashSet<String>(), "/fo*", handlerMethod1);
|
mapping.registerHandlerMethod(handler, method1, "/fo*");
|
||||||
mapping.registerHandlerMethod(new HashSet<String>(), "/f*", handlerMethod1);
|
mapping.registerHandlerMethod(handler, method1, "/f*");
|
||||||
|
|
||||||
HandlerMethod result = mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/foo"));
|
HandlerMethod result = mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/foo"));
|
||||||
assertEquals(handlerMethod1, result);
|
assertEquals(method1, result.getMethod());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalStateException.class)
|
@Test(expected = IllegalStateException.class)
|
||||||
public void ambiguousMatch() throws Exception {
|
public void ambiguousMatch() throws Exception {
|
||||||
mapping.registerHandlerMethod(new HashSet<String>(), "/f?o", handlerMethod1);
|
mapping.registerHandlerMethod(handler, method1, "/f?o");
|
||||||
mapping.registerHandlerMethod(new HashSet<String>(), "/fo?", handlerMethod2);
|
mapping.registerHandlerMethod(handler, method2, "/fo?");
|
||||||
|
|
||||||
mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/foo"));
|
mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/foo"));
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +97,7 @@ public class HandlerMethodMappingTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getMappingForMethod(String beanName, Method method) {
|
protected String getMappingForMethod(Method method, Class<?> handlerType) {
|
||||||
String methodName = method.getName();
|
String methodName = method.getName();
|
||||||
return methodName.startsWith("handler") ? methodName : null;
|
return methodName.startsWith("handler") ? methodName : null;
|
||||||
}
|
}
|
||||||
|
|
@ -106,7 +108,7 @@ public class HandlerMethodMappingTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isHandler(String beanName) {
|
protected boolean isHandler(Class<?> beanType) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1531,6 +1531,7 @@ public class ServletHandlerMethodTests {
|
||||||
|
|
||||||
@RequestMapping("/myPage")
|
@RequestMapping("/myPage")
|
||||||
@SessionAttributes({"object1", "object2"})
|
@SessionAttributes({"object1", "object2"})
|
||||||
|
@Controller
|
||||||
public interface MySessionAttributesControllerIfc {
|
public interface MySessionAttributesControllerIfc {
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET)
|
@RequestMapping(method = RequestMethod.GET)
|
||||||
|
|
@ -1540,7 +1541,6 @@ public class ServletHandlerMethodTests {
|
||||||
String post(@ModelAttribute("object1") Object object1);
|
String post(@ModelAttribute("object1") Object object1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Controller
|
|
||||||
public static class MySessionAttributesControllerImpl implements MySessionAttributesControllerIfc {
|
public static class MySessionAttributesControllerImpl implements MySessionAttributesControllerIfc {
|
||||||
|
|
||||||
public String get(Model model) {
|
public String get(Model model) {
|
||||||
|
|
|
||||||
|
|
@ -542,6 +542,7 @@ public class UriTemplateServletHandlerMethodTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Controller
|
||||||
@RequestMapping("/*/menu/**")
|
@RequestMapping("/*/menu/**")
|
||||||
public static class MenuTreeController {
|
public static class MenuTreeController {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue