Browse Source

Detect controller methods via InitializingBean hook

Previously RequestMappingHandlerMapping detected @RequestMapping
methods through an initApplicationContext() hook. However, the
HandlerMapping may not have been fully set up with all its
dependencies at that point including settings like useSuffixPattern
and others.

This change moves the detection @RequestMapping methods to an
InitializingBean.afterPropertiesSet() hook.

Issue: SPR-9371
pull/77/head
Rossen Stoyanchev 13 years ago
parent
commit
d7efc0db80
  1. 59
      spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java
  2. 2
      spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java
  3. 12
      spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.java
  4. 3
      spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HandlerMethodAnnotationDetectionTests.java
  5. 1
      src/dist/changelog.txt

59
spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
@ -30,6 +30,7 @@ import javax.servlet.ServletException; @@ -30,6 +30,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContextException;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
@ -42,18 +43,18 @@ import org.springframework.web.servlet.HandlerMapping; @@ -42,18 +43,18 @@ import org.springframework.web.servlet.HandlerMapping;
/**
* Abstract base class for {@link HandlerMapping} implementations that define a
* mapping between a request and a {@link HandlerMethod}.
*
* <p>For each registered handler method, a unique mapping is maintained with
* subclasses defining the details of the mapping type {@code <T>}.
*
*
* <p>For each registered handler method, a unique mapping is maintained with
* subclasses defining the details of the mapping type {@code <T>}.
*
* @param <T> The mapping for a {@link HandlerMethod} containing the conditions
* needed to match the handler method to incoming request.
*
* needed to match the handler method to incoming request.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.1
*/
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping {
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
private boolean detectHandlerMethodsInAncestorContexts = false;
@ -72,7 +73,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap @@ -72,7 +73,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
public void setDetectHandlerMethodsInAncestorContexts(boolean detectHandlerMethodsInAncestorContexts) {
this.detectHandlerMethodsInAncestorContexts = detectHandlerMethodsInAncestorContexts;
}
/**
* Return a map with all handler methods and their mappings.
*/
@ -81,11 +82,17 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap @@ -81,11 +82,17 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
}
/**
* ApplicationContext initialization and handler method detection.
* ApplicationContext initialization.
*/
@Override
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
}
/**
* Detects handler methods at initialization.
*/
public void afterPropertiesSet() {
initHandlerMethods();
}
@ -99,7 +106,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap @@ -99,7 +106,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
@ -131,17 +138,17 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap @@ -131,17 +138,17 @@ 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) ?
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() {
public boolean matches(Method method) {
return getMappingForMethod(method, userType) != null;
}
});
for (Method method : methods) {
T mapping = getMappingForMethod(method, userType);
registerHandlerMethod(handler, method, mapping);
@ -149,7 +156,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap @@ -149,7 +156,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
}
/**
* Provide the mapping for a handler method. A method for which no
* Provide the mapping for a handler method. A method for which no
* mapping can be provided is not a handler method.
*
* @param method the method to provide a mapping for
@ -161,11 +168,11 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap @@ -161,11 +168,11 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
/**
* Register a handler method and its unique mapping.
*
*
* @param handler the bean name of the handler or the handler instance
* @param method the method to register
* @param mapping the mapping conditions associated with the handler method
* @throws IllegalStateException if another method was already registered
* @throws IllegalStateException if another method was already registered
* under the same mapping
*/
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
@ -177,19 +184,19 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap @@ -177,19 +184,19 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
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()
+ "' bean method \n" + handlerMethod + "\nto " + mapping + ": There is already '"
+ oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
}
handlerMethods.put(mapping, handlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
}
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
@ -199,7 +206,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap @@ -199,7 +206,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
}
/**
* Extract and return the URL paths contained in a mapping.
* Extract and return the URL paths contained in a mapping.
*/
protected abstract Set<String> getMappingPathPatterns(T mapping);
@ -230,11 +237,11 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap @@ -230,11 +237,11 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
/**
* Look up the best-matching handler method for the current request.
* If multiple matches are found, the best match is selected.
*
*
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @return the best-matching handler method, or {@code null} if no match
*
*
* @see #handleMatch(Object, String, HttpServletRequest)
* @see #handleNoMatch(Set, String, HttpServletRequest)
*/
@ -289,7 +296,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap @@ -289,7 +296,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
}
/**
* Check if a mapping matches the current request and return a (potentially
* Check if a mapping matches the current request and return a (potentially
* new) mapping with conditions relevant to the current request.
*
* @param mapping the mapping to get a match for
@ -308,7 +315,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap @@ -308,7 +315,7 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
/**
* Invoked when a matching mapping is found.
* @param mapping the matching mapping
* @param mapping the matching mapping
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
*/
@ -360,5 +367,5 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap @@ -360,5 +367,5 @@ public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMap
return comparator.compare(match1.mapping, match2.mapping);
}
}
}

2
spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupportTests.java

@ -83,6 +83,7 @@ public class WebMvcConfigurationSupportTests { @@ -83,6 +83,7 @@ public class WebMvcConfigurationSupportTests {
assertEquals(0, handlerMapping.getOrder());
handlerMapping.setApplicationContext(cxt);
handlerMapping.afterPropertiesSet();
HandlerExecutionChain chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/"));
assertNotNull(chain.getInterceptors());
assertEquals(ConversionServiceExposingInterceptor.class, chain.getInterceptors()[0].getClass());
@ -204,6 +205,7 @@ public class WebMvcConfigurationSupportTests { @@ -204,6 +205,7 @@ public class WebMvcConfigurationSupportTests {
RequestMappingHandlerMapping rmHandlerMapping = webConfig.requestMappingHandlerMapping();
rmHandlerMapping.setApplicationContext(appCxt);
rmHandlerMapping.afterPropertiesSet();
HandlerExecutionChain chain = rmHandlerMapping.getHandler(new MockHttpServletRequest("GET", "/"));
assertNotNull(chain.getInterceptors());
assertEquals(2, chain.getInterceptors().length);

12
spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
@ -82,7 +82,7 @@ public class HandlerMethodMappingTests { @@ -82,7 +82,7 @@ public class HandlerMethodMappingTests {
HandlerMethod result = mapping.getHandlerInternal(new MockHttpServletRequest("GET", "/foo"));
assertEquals(method1, result.getMethod());
}
@Test(expected = IllegalStateException.class)
public void ambiguousMatch() throws Exception {
mapping.registerHandlerMethod(handler, method1, "/f?o");
@ -95,24 +95,26 @@ public class HandlerMethodMappingTests { @@ -95,24 +95,26 @@ public class HandlerMethodMappingTests {
public void testDetectHandlerMethodsInAncestorContexts() {
StaticApplicationContext cxt = new StaticApplicationContext();
cxt.registerSingleton("myHandler", MyHandler.class);
AbstractHandlerMethodMapping<String> mapping1 = new MyHandlerMethodMapping();
mapping1.setApplicationContext(new StaticApplicationContext(cxt));
mapping1.afterPropertiesSet();
assertEquals(0, mapping1.getHandlerMethods().size());
AbstractHandlerMethodMapping<String> mapping2 = new MyHandlerMethodMapping();
mapping2.setDetectHandlerMethodsInAncestorContexts(true);
mapping2.setApplicationContext(new StaticApplicationContext(cxt));
mapping2.afterPropertiesSet();
assertEquals(2, mapping2.getHandlerMethods().size());
}
private static class MyHandlerMethodMapping extends AbstractHandlerMethodMapping<String> {
private UrlPathHelper pathHelper = new UrlPathHelper();
private PathMatcher pathMatcher = new AntPathMatcher();
@Override

3
spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/HandlerMethodAnnotationDetectionTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
@ -103,6 +103,7 @@ public class HandlerMethodAnnotationDetectionTests { @@ -103,6 +103,7 @@ public class HandlerMethodAnnotationDetectionTests {
context.refresh();
handlerMapping.setApplicationContext(context);
handlerMapping.afterPropertiesSet();
handlerAdapter.afterPropertiesSet();
exceptionResolver.afterPropertiesSet();
}

1
src/dist/changelog.txt vendored

@ -16,6 +16,7 @@ Changes in version 3.2 M1 @@ -16,6 +16,7 @@ Changes in version 3.2 M1
* add Jackson 2 HttpMessageConverter and View types
* add pretty print option to Jackson HttpMessageConverter and View types
* fix issue with resolving Errors controller method argument
* detect controller methods via InitializingBean in RequestMappingHandlerMapping
Changes in version 3.1.1 (2012-02-16)
-------------------------------------

Loading…
Cancel
Save