added order property to AnnotationMethodHandlerAdapter (SPR-6516)
This commit is contained in:
parent
abf6a7ee18
commit
f72769a621
|
|
@ -59,6 +59,7 @@ import org.springframework.beans.factory.config.BeanExpressionContext;
|
||||||
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
||||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.core.style.StylerUtils;
|
import org.springframework.core.style.StylerUtils;
|
||||||
|
|
@ -117,7 +118,8 @@ import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
|
||||||
* @see #setWebBindingInitializer
|
* @see #setWebBindingInitializer
|
||||||
* @see #setSessionAttributeStore
|
* @see #setSessionAttributeStore
|
||||||
*/
|
*/
|
||||||
public class AnnotationMethodHandlerAdapter extends PortletContentGenerator implements HandlerAdapter, BeanFactoryAware {
|
public class AnnotationMethodHandlerAdapter extends PortletContentGenerator
|
||||||
|
implements HandlerAdapter, Ordered, BeanFactoryAware {
|
||||||
|
|
||||||
private static final String IMPLICIT_MODEL_ATTRIBUTE = "org.springframework.web.portlet.mvc.ImplicitModel";
|
private static final String IMPLICIT_MODEL_ATTRIBUTE = "org.springframework.web.portlet.mvc.ImplicitModel";
|
||||||
|
|
||||||
|
|
@ -136,6 +138,8 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl
|
||||||
|
|
||||||
private ModelAndViewResolver[] customModelAndViewResolvers;
|
private ModelAndViewResolver[] customModelAndViewResolvers;
|
||||||
|
|
||||||
|
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||||
|
|
||||||
private ConfigurableBeanFactory beanFactory;
|
private ConfigurableBeanFactory beanFactory;
|
||||||
|
|
||||||
private BeanExpressionContext expressionContext;
|
private BeanExpressionContext expressionContext;
|
||||||
|
|
@ -242,6 +246,19 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl
|
||||||
this.customModelAndViewResolvers = customModelAndViewResolvers;
|
this.customModelAndViewResolvers = customModelAndViewResolvers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the order value for this HandlerAdapter bean.
|
||||||
|
* <p>Default value is <code>Integer.MAX_VALUE</code>, meaning that it's non-ordered.
|
||||||
|
* @see org.springframework.core.Ordered#getOrder()
|
||||||
|
*/
|
||||||
|
public void setOrder(int order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrder() {
|
||||||
|
return this.order;
|
||||||
|
}
|
||||||
|
|
||||||
public void setBeanFactory(BeanFactory beanFactory) {
|
public void setBeanFactory(BeanFactory beanFactory) {
|
||||||
if (beanFactory instanceof ConfigurableBeanFactory) {
|
if (beanFactory instanceof ConfigurableBeanFactory) {
|
||||||
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
|
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
|
||||||
|
|
@ -362,6 +379,9 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Portlet-specific subclass of {@link HandlerMethodResolver}.
|
||||||
|
*/
|
||||||
private static class PortletHandlerMethodResolver extends HandlerMethodResolver {
|
private static class PortletHandlerMethodResolver extends HandlerMethodResolver {
|
||||||
|
|
||||||
private final Map<Method, RequestMappingInfo> mappings = new HashMap<Method, RequestMappingInfo>();
|
private final Map<Method, RequestMappingInfo> mappings = new HashMap<Method, RequestMappingInfo>();
|
||||||
|
|
@ -467,100 +487,9 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class RequestMappingInfo {
|
/**
|
||||||
|
* Portlet-specific subclass of {@link HandlerMethodInvoker}.
|
||||||
public final Set<PortletMode> modes = new HashSet<PortletMode>();
|
*/
|
||||||
|
|
||||||
public String phase;
|
|
||||||
|
|
||||||
public String value;
|
|
||||||
|
|
||||||
public final Set<String> methods = new HashSet<String>();
|
|
||||||
|
|
||||||
public String[] params = new String[0];
|
|
||||||
|
|
||||||
public String[] headers = new String[0];
|
|
||||||
|
|
||||||
public void initStandardMapping(String[] modes, RequestMethod[] methods, String[] params, String[] headers) {
|
|
||||||
for (String mode : modes) {
|
|
||||||
this.modes.add(new PortletMode(mode));
|
|
||||||
}
|
|
||||||
for (RequestMethod method : methods) {
|
|
||||||
this.methods.add(method.name());
|
|
||||||
}
|
|
||||||
this.params = StringUtils.mergeStringArrays(this.params, params);
|
|
||||||
this.headers = StringUtils.mergeStringArrays(this.headers, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initPhaseMapping(String phase, String value, String[] params) {
|
|
||||||
if (this.phase != null) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Invalid mapping - more than one phase specified: '" + this.phase + "', '" + phase + "'");
|
|
||||||
}
|
|
||||||
this.phase = phase;
|
|
||||||
this.value = value;
|
|
||||||
this.params = StringUtils.mergeStringArrays(this.params, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean match(PortletRequest request) {
|
|
||||||
if (!this.modes.isEmpty() && !this.modes.contains(request.getPortletMode())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (StringUtils.hasLength(this.phase) &&
|
|
||||||
!this.phase.equals(request.getAttribute(PortletRequest.LIFECYCLE_PHASE))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (StringUtils.hasLength(this.value)) {
|
|
||||||
if (this.phase.equals(PortletRequest.ACTION_PHASE) &&
|
|
||||||
!this.value.equals(request.getParameter(ActionRequest.ACTION_NAME))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (this.phase.equals(PortletRequest.RENDER_PHASE) &&
|
|
||||||
!(new WindowState(this.value)).equals(request.getWindowState())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (this.phase.equals(PortletRequest.RESOURCE_PHASE) &&
|
|
||||||
!this.value.equals(((ResourceRequest) request).getResourceID())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (this.phase.equals(PortletRequest.EVENT_PHASE)) {
|
|
||||||
Event event = ((EventRequest) request).getEvent();
|
|
||||||
if (!this.value.equals(event.getName()) && !this.value.equals(event.getQName().toString())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return PortletAnnotationMappingUtils.checkRequestMethod(this.methods, request) &&
|
|
||||||
PortletAnnotationMappingUtils.checkParameters(this.params, request) &&
|
|
||||||
PortletAnnotationMappingUtils.checkHeaders(this.headers, request);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBetterMatchThan(RequestMappingInfo other) {
|
|
||||||
return ((!this.modes.isEmpty() && other.modes.isEmpty()) ||
|
|
||||||
(StringUtils.hasLength(this.phase) && !StringUtils.hasLength(other.phase)) ||
|
|
||||||
(StringUtils.hasLength(this.value) && !StringUtils.hasLength(other.value)) ||
|
|
||||||
(!this.methods.isEmpty() && other.methods.isEmpty()) ||
|
|
||||||
this.params.length > other.params.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
RequestMappingInfo other = (RequestMappingInfo) obj;
|
|
||||||
return (this.modes.equals(other.modes) &&
|
|
||||||
ObjectUtils.nullSafeEquals(this.phase, other.phase) &&
|
|
||||||
ObjectUtils.nullSafeEquals(this.value, other.value) &&
|
|
||||||
this.methods.equals(other.methods) &&
|
|
||||||
Arrays.equals(this.params, other.params) &&
|
|
||||||
Arrays.equals(this.headers, other.headers));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return (ObjectUtils.nullSafeHashCode(this.modes) * 29 + this.phase.hashCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private class PortletHandlerMethodInvoker extends HandlerMethodInvoker {
|
private class PortletHandlerMethodInvoker extends HandlerMethodInvoker {
|
||||||
|
|
||||||
public PortletHandlerMethodInvoker(HandlerMethodResolver resolver) {
|
public PortletHandlerMethodInvoker(HandlerMethodResolver resolver) {
|
||||||
|
|
@ -735,4 +664,101 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holder for request mapping metadata. Allows for finding a best matching candidate.
|
||||||
|
*/
|
||||||
|
private static class RequestMappingInfo {
|
||||||
|
|
||||||
|
public final Set<PortletMode> modes = new HashSet<PortletMode>();
|
||||||
|
|
||||||
|
public String phase;
|
||||||
|
|
||||||
|
public String value;
|
||||||
|
|
||||||
|
public final Set<String> methods = new HashSet<String>();
|
||||||
|
|
||||||
|
public String[] params = new String[0];
|
||||||
|
|
||||||
|
public String[] headers = new String[0];
|
||||||
|
|
||||||
|
public void initStandardMapping(String[] modes, RequestMethod[] methods, String[] params, String[] headers) {
|
||||||
|
for (String mode : modes) {
|
||||||
|
this.modes.add(new PortletMode(mode));
|
||||||
|
}
|
||||||
|
for (RequestMethod method : methods) {
|
||||||
|
this.methods.add(method.name());
|
||||||
|
}
|
||||||
|
this.params = StringUtils.mergeStringArrays(this.params, params);
|
||||||
|
this.headers = StringUtils.mergeStringArrays(this.headers, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initPhaseMapping(String phase, String value, String[] params) {
|
||||||
|
if (this.phase != null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Invalid mapping - more than one phase specified: '" + this.phase + "', '" + phase + "'");
|
||||||
|
}
|
||||||
|
this.phase = phase;
|
||||||
|
this.value = value;
|
||||||
|
this.params = StringUtils.mergeStringArrays(this.params, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean match(PortletRequest request) {
|
||||||
|
if (!this.modes.isEmpty() && !this.modes.contains(request.getPortletMode())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (StringUtils.hasLength(this.phase) &&
|
||||||
|
!this.phase.equals(request.getAttribute(PortletRequest.LIFECYCLE_PHASE))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (StringUtils.hasLength(this.value)) {
|
||||||
|
if (this.phase.equals(PortletRequest.ACTION_PHASE) &&
|
||||||
|
!this.value.equals(request.getParameter(ActionRequest.ACTION_NAME))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (this.phase.equals(PortletRequest.RENDER_PHASE) &&
|
||||||
|
!(new WindowState(this.value)).equals(request.getWindowState())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (this.phase.equals(PortletRequest.RESOURCE_PHASE) &&
|
||||||
|
!this.value.equals(((ResourceRequest) request).getResourceID())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (this.phase.equals(PortletRequest.EVENT_PHASE)) {
|
||||||
|
Event event = ((EventRequest) request).getEvent();
|
||||||
|
if (!this.value.equals(event.getName()) && !this.value.equals(event.getQName().toString())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PortletAnnotationMappingUtils.checkRequestMethod(this.methods, request) &&
|
||||||
|
PortletAnnotationMappingUtils.checkParameters(this.params, request) &&
|
||||||
|
PortletAnnotationMappingUtils.checkHeaders(this.headers, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBetterMatchThan(RequestMappingInfo other) {
|
||||||
|
return ((!this.modes.isEmpty() && other.modes.isEmpty()) ||
|
||||||
|
(StringUtils.hasLength(this.phase) && !StringUtils.hasLength(other.phase)) ||
|
||||||
|
(StringUtils.hasLength(this.value) && !StringUtils.hasLength(other.value)) ||
|
||||||
|
(!this.methods.isEmpty() && other.methods.isEmpty()) ||
|
||||||
|
this.params.length > other.params.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
RequestMappingInfo other = (RequestMappingInfo) obj;
|
||||||
|
return (this.modes.equals(other.modes) &&
|
||||||
|
ObjectUtils.nullSafeEquals(this.phase, other.phase) &&
|
||||||
|
ObjectUtils.nullSafeEquals(this.value, other.value) &&
|
||||||
|
this.methods.equals(other.methods) &&
|
||||||
|
Arrays.equals(this.params, other.params) &&
|
||||||
|
Arrays.equals(this.headers, other.headers));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return (ObjectUtils.nullSafeHashCode(this.modes) * 29 + this.phase.hashCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ import org.springframework.beans.factory.config.BeanExpressionContext;
|
||||||
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
||||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.http.HttpInputMessage;
|
import org.springframework.http.HttpInputMessage;
|
||||||
|
|
@ -127,22 +128,22 @@ import org.springframework.web.util.WebUtils;
|
||||||
* @see #setSessionAttributeStore
|
* @see #setSessionAttributeStore
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
public class AnnotationMethodHandlerAdapter extends WebContentGenerator implements HandlerAdapter, BeanFactoryAware {
|
public class AnnotationMethodHandlerAdapter extends WebContentGenerator
|
||||||
|
implements HandlerAdapter, Ordered, BeanFactoryAware {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log category to use when no mapped handler is found for a request.
|
* Log category to use when no mapped handler is found for a request.
|
||||||
*
|
|
||||||
* @see #pageNotFoundLogger
|
* @see #pageNotFoundLogger
|
||||||
*/
|
*/
|
||||||
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
|
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional logger to use when no mapped handler is found for a request.
|
* Additional logger to use when no mapped handler is found for a request.
|
||||||
*
|
|
||||||
* @see #PAGE_NOT_FOUND_LOG_CATEGORY
|
* @see #PAGE_NOT_FOUND_LOG_CATEGORY
|
||||||
*/
|
*/
|
||||||
protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
|
protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
|
||||||
|
|
||||||
|
|
||||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||||
|
|
||||||
private PathMatcher pathMatcher = new AntPathMatcher();
|
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||||
|
|
@ -167,6 +168,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
new HttpMessageConverter[]{new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter(),
|
new HttpMessageConverter[]{new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter(),
|
||||||
new FormHttpMessageConverter(), new SourceHttpMessageConverter()};
|
new FormHttpMessageConverter(), new SourceHttpMessageConverter()};
|
||||||
|
|
||||||
|
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||||
|
|
||||||
private ConfigurableBeanFactory beanFactory;
|
private ConfigurableBeanFactory beanFactory;
|
||||||
|
|
||||||
private BeanExpressionContext expressionContext;
|
private BeanExpressionContext expressionContext;
|
||||||
|
|
@ -174,16 +177,18 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
private final Map<Class<?>, ServletHandlerMethodResolver> methodResolverCache =
|
private final Map<Class<?>, ServletHandlerMethodResolver> methodResolverCache =
|
||||||
new ConcurrentHashMap<Class<?>, ServletHandlerMethodResolver>();
|
new ConcurrentHashMap<Class<?>, ServletHandlerMethodResolver>();
|
||||||
|
|
||||||
|
|
||||||
public AnnotationMethodHandlerAdapter() {
|
public AnnotationMethodHandlerAdapter() {
|
||||||
// no restriction of HTTP methods by default
|
// no restriction of HTTP methods by default
|
||||||
super(false);
|
super(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set if URL lookup should always use the full path within the current servlet context. Else, the path within the
|
* Set if URL lookup should always use the full path within the current servlet
|
||||||
* current servlet mapping is used if applicable (that is, in the case of a ".../*" servlet mapping in web.xml).
|
* context. Else, the path within the current servlet mapping is used if applicable
|
||||||
|
* (that is, in the case of a ".../*" servlet mapping in web.xml).
|
||||||
* <p>Default is "false".
|
* <p>Default is "false".
|
||||||
*
|
|
||||||
* @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
|
* @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
|
||||||
*/
|
*/
|
||||||
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
|
public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
|
||||||
|
|
@ -191,10 +196,10 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set if context path and request URI should be URL-decoded. Both are returned <i>undecoded</i> by the Servlet API, in
|
* Set if context path and request URI should be URL-decoded. Both are returned
|
||||||
* contrast to the servlet path. <p>Uses either the request encoding or the default encoding according to the Servlet
|
* <i>undecoded</i> by the Servlet API, in contrast to the servlet path.
|
||||||
* spec (ISO-8859-1).
|
* <p>Uses either the request encoding or the default encoding according
|
||||||
*
|
* to the Servlet spec (ISO-8859-1).
|
||||||
* @see org.springframework.web.util.UrlPathHelper#setUrlDecode
|
* @see org.springframework.web.util.UrlPathHelper#setUrlDecode
|
||||||
*/
|
*/
|
||||||
public void setUrlDecode(boolean urlDecode) {
|
public void setUrlDecode(boolean urlDecode) {
|
||||||
|
|
@ -202,8 +207,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the UrlPathHelper to use for resolution of lookup paths. <p>Use this to override the default UrlPathHelper with
|
* Set the UrlPathHelper to use for resolution of lookup paths.
|
||||||
* a custom subclass, or to share common UrlPathHelper settings across multiple HandlerMappings and HandlerAdapters.
|
* <p>Use this to override the default UrlPathHelper with a custom subclass,
|
||||||
|
* or to share common UrlPathHelper settings across multiple HandlerMappings and HandlerAdapters.
|
||||||
*/
|
*/
|
||||||
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
|
||||||
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
|
Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
|
||||||
|
|
@ -211,10 +217,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the PathMatcher implementation to use for matching URL paths against registered URL patterns. Default is
|
* Set the PathMatcher implementation to use for matching URL paths against registered URL patterns.
|
||||||
* AntPathMatcher.
|
* <p>Default is {@link org.springframework.util.AntPathMatcher}.
|
||||||
*
|
|
||||||
* @see org.springframework.util.AntPathMatcher
|
|
||||||
*/
|
*/
|
||||||
public void setPathMatcher(PathMatcher pathMatcher) {
|
public void setPathMatcher(PathMatcher pathMatcher) {
|
||||||
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
Assert.notNull(pathMatcher, "PathMatcher must not be null");
|
||||||
|
|
@ -222,8 +226,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the MethodNameResolver to use for resolving default handler methods (carrying an empty
|
* Set the MethodNameResolver to use for resolving default handler methods
|
||||||
* <code>@RequestMapping</code> annotation). <p>Will only kick in when the handler method cannot be resolved uniquely
|
* (carrying an empty <code>@RequestMapping</code> annotation).
|
||||||
|
* <p>Will only kick in when the handler method cannot be resolved uniquely
|
||||||
* through the annotation metadata already.
|
* through the annotation metadata already.
|
||||||
*/
|
*/
|
||||||
public void setMethodNameResolver(MethodNameResolver methodNameResolver) {
|
public void setMethodNameResolver(MethodNameResolver methodNameResolver) {
|
||||||
|
|
@ -231,15 +236,16 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify a WebBindingInitializer which will apply pre-configured configuration to every DataBinder that this
|
* Specify a WebBindingInitializer which will apply pre-configured
|
||||||
* controller uses.
|
* configuration to every DataBinder that this controller uses.
|
||||||
*/
|
*/
|
||||||
public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
|
public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
|
||||||
this.webBindingInitializer = webBindingInitializer;
|
this.webBindingInitializer = webBindingInitializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify the strategy to store session attributes with. <p>Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore},
|
* Specify the strategy to store session attributes with.
|
||||||
|
* <p>Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore},
|
||||||
* storing session attributes in the HttpSession, using the same attribute name as in the model.
|
* storing session attributes in the HttpSession, using the same attribute name as in the model.
|
||||||
*/
|
*/
|
||||||
public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
|
public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
|
||||||
|
|
@ -248,11 +254,11 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache content produced by <code>@SessionAttributes</code> annotated handlers for the given number of seconds.
|
* Cache content produced by <code>@SessionAttributes</code> annotated handlers
|
||||||
* Default is 0, preventing caching completely. <p>In contrast to the "cacheSeconds" property which will apply to all
|
* for the given number of seconds. Default is 0, preventing caching completely.
|
||||||
* general handlers (but not to <code>@SessionAttributes</code> annotated handlers), this setting will apply to
|
* <p>In contrast to the "cacheSeconds" property which will apply to all general handlers
|
||||||
* <code>@SessionAttributes</code> annotated handlers only.
|
* (but not to <code>@SessionAttributes</code> annotated handlers), this setting will
|
||||||
*
|
* apply to <code>@SessionAttributes</code> annotated handlers only.
|
||||||
* @see #setCacheSeconds
|
* @see #setCacheSeconds
|
||||||
* @see org.springframework.web.bind.annotation.SessionAttributes
|
* @see org.springframework.web.bind.annotation.SessionAttributes
|
||||||
*/
|
*/
|
||||||
|
|
@ -261,15 +267,20 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set if controller execution should be synchronized on the session, to serialize parallel invocations from the same
|
* Set if controller execution should be synchronized on the session,
|
||||||
* client. <p>More specifically, the execution of each handler method will get synchronized if this flag is "true". The
|
* to serialize parallel invocations from the same client.
|
||||||
* best available session mutex will be used for the synchronization; ideally, this will be a mutex exposed by
|
* <p>More specifically, the execution of the <code>handleRequestInternal</code>
|
||||||
* HttpSessionMutexListener. <p>The session mutex is guaranteed to be the same object during the entire lifetime of the
|
* method will get synchronized if this flag is "true". The best available
|
||||||
* session, available under the key defined by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a safe
|
* session mutex will be used for the synchronization; ideally, this will
|
||||||
* reference to synchronize on for locking on the current session. <p>In many cases, the HttpSession reference itself a
|
* be a mutex exposed by HttpSessionMutexListener.
|
||||||
* safe mutex as well, since it will always be the same object reference for the same active logical session. However,
|
* <p>The session mutex is guaranteed to be the same object during
|
||||||
* this is not guaranteed across different servlet containers; the only 100% safe way is a session mutex.
|
* the entire lifetime of the session, available under the key defined
|
||||||
*
|
* by the <code>SESSION_MUTEX_ATTRIBUTE</code> constant. It serves as a
|
||||||
|
* safe reference to synchronize on for locking on the current session.
|
||||||
|
* <p>In many cases, the HttpSession reference itself is a safe mutex
|
||||||
|
* as well, since it will always be the same object reference for the
|
||||||
|
* same active logical session. However, this is not guaranteed across
|
||||||
|
* different servlet containers; the only 100% safe way is a session mutex.
|
||||||
* @see org.springframework.web.util.HttpSessionMutexListener
|
* @see org.springframework.web.util.HttpSessionMutexListener
|
||||||
* @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)
|
* @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)
|
||||||
*/
|
*/
|
||||||
|
|
@ -278,61 +289,76 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the ParameterNameDiscoverer to use for resolving method parameter names if needed (e.g. for default attribute
|
* Set the ParameterNameDiscoverer to use for resolving method parameter names if needed
|
||||||
* names). <p>Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
|
* (e.g. for default attribute names).
|
||||||
|
* <p>Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
|
||||||
*/
|
*/
|
||||||
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
|
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||||
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a custom WebArgumentResolvers to use for special method parameter types. Such a custom WebArgumentResolver will
|
* Set a custom WebArgumentResolvers to use for special method parameter types.
|
||||||
* kick in first, having a chance to resolve an argument value before the standard argument handling kicks in.
|
* <p>Such a custom WebArgumentResolver will kick in first, having a chance to resolve
|
||||||
|
* an argument value before the standard argument handling kicks in.
|
||||||
*/
|
*/
|
||||||
public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) {
|
public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) {
|
||||||
this.customArgumentResolvers = new WebArgumentResolver[] {argumentResolver};
|
this.customArgumentResolvers = new WebArgumentResolver[] {argumentResolver};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set one or more custom WebArgumentResolvers to use for special method parameter types. Any such custom
|
* Set one or more custom WebArgumentResolvers to use for special method parameter types.
|
||||||
* WebArgumentResolver will kick in first, having a chance to resolve an argument value before the standard argument
|
* <p>Any such custom WebArgumentResolver will kick in first, having a chance to resolve
|
||||||
* handling kicks in.
|
* an argument value before the standard argument handling kicks in.
|
||||||
*/
|
*/
|
||||||
public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
|
public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
|
||||||
this.customArgumentResolvers = argumentResolvers;
|
this.customArgumentResolvers = argumentResolvers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a custom ModelAndViewResolvers to use for special method return types. Such a custom ModelAndViewResolver will
|
* Set a custom ModelAndViewResolvers to use for special method return types.
|
||||||
* kick in first, having a chance to resolve an return value before the standard ModelAndView handling kicks in.
|
* <p>Such a custom ModelAndViewResolver will kick in first, having a chance to resolve
|
||||||
|
* a return value before the standard ModelAndView handling kicks in.
|
||||||
*/
|
*/
|
||||||
public void setCustomModelAndViewResolver(ModelAndViewResolver customModelAndViewResolver) {
|
public void setCustomModelAndViewResolver(ModelAndViewResolver customModelAndViewResolver) {
|
||||||
this.customModelAndViewResolvers = new ModelAndViewResolver[] {customModelAndViewResolver};
|
this.customModelAndViewResolvers = new ModelAndViewResolver[] {customModelAndViewResolver};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set one or more custom ModelAndViewResolvers to use for special method return types. Any such custom
|
* Set one or more custom ModelAndViewResolvers to use for special method return types.
|
||||||
* ModelAndViewResolver will kick in first, having a chance to resolve an return value before the standard ModelAndView
|
* <p>Any such custom ModelAndViewResolver will kick in first, having a chance to resolve
|
||||||
* handling kicks in.
|
* a return value before the standard ModelAndView handling kicks in.
|
||||||
*/
|
*/
|
||||||
public void setCustomModelAndViewResolvers(ModelAndViewResolver[] customModelAndViewResolvers) {
|
public void setCustomModelAndViewResolvers(ModelAndViewResolver[] customModelAndViewResolvers) {
|
||||||
this.customModelAndViewResolvers = customModelAndViewResolvers;
|
this.customModelAndViewResolvers = customModelAndViewResolvers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the message body converters to use. These converters are used to convert from and to HTTP requests and
|
* Set the message body converters to use.
|
||||||
* responses.
|
* <p>These converters are used to convert from and to HTTP requests and responses.
|
||||||
|
*/
|
||||||
|
public void setMessageConverters(HttpMessageConverter<?>[] messageConverters) {
|
||||||
|
this.messageConverters = messageConverters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the message body converters that this adapter has been configured with.
|
||||||
*/
|
*/
|
||||||
public HttpMessageConverter<?>[] getMessageConverters() {
|
public HttpMessageConverter<?>[] getMessageConverters() {
|
||||||
return messageConverters;
|
return messageConverters;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the message body converters to use. These converters are used to convert from and to HTTP requests and
|
* Specify the order value for this HandlerAdapter bean.
|
||||||
* responses.
|
* <p>Default value is <code>Integer.MAX_VALUE</code>, meaning that it's non-ordered.
|
||||||
|
* @see org.springframework.core.Ordered#getOrder()
|
||||||
*/
|
*/
|
||||||
public void setMessageConverters(HttpMessageConverter<?>[] messageConverters) {
|
public void setOrder(int order) {
|
||||||
this.messageConverters = messageConverters;
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrder() {
|
||||||
|
return this.order;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBeanFactory(BeanFactory beanFactory) {
|
public void setBeanFactory(BeanFactory beanFactory) {
|
||||||
|
|
@ -342,6 +368,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean supports(Object handler) {
|
public boolean supports(Object handler) {
|
||||||
return getMethodResolver(handler).hasHandlerMethods();
|
return getMethodResolver(handler).hasHandlerMethods();
|
||||||
}
|
}
|
||||||
|
|
@ -393,7 +420,9 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Build a HandlerMethodResolver for the given handler type. */
|
/**
|
||||||
|
* Build a HandlerMethodResolver for the given handler type.
|
||||||
|
*/
|
||||||
private ServletHandlerMethodResolver getMethodResolver(Object handler) {
|
private ServletHandlerMethodResolver getMethodResolver(Object handler) {
|
||||||
Class handlerClass = ClassUtils.getUserClass(handler);
|
Class handlerClass = ClassUtils.getUserClass(handler);
|
||||||
ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
|
ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
|
||||||
|
|
@ -404,7 +433,10 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
return resolver;
|
return resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Servlet-specific subclass of {@link HandlerMethodResolver}. */
|
|
||||||
|
/**
|
||||||
|
* Servlet-specific subclass of {@link HandlerMethodResolver}.
|
||||||
|
*/
|
||||||
private class ServletHandlerMethodResolver extends HandlerMethodResolver {
|
private class ServletHandlerMethodResolver extends HandlerMethodResolver {
|
||||||
|
|
||||||
private ServletHandlerMethodResolver(Class<?> handlerType) {
|
private ServletHandlerMethodResolver(Class<?> handlerType) {
|
||||||
|
|
@ -606,14 +638,17 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Servlet-specific subclass of {@link HandlerMethodInvoker}. */
|
|
||||||
|
/**
|
||||||
|
* Servlet-specific subclass of {@link HandlerMethodInvoker}.
|
||||||
|
*/
|
||||||
private class ServletHandlerMethodInvoker extends HandlerMethodInvoker {
|
private class ServletHandlerMethodInvoker extends HandlerMethodInvoker {
|
||||||
|
|
||||||
private boolean responseArgumentUsed = false;
|
private boolean responseArgumentUsed = false;
|
||||||
|
|
||||||
private ServletHandlerMethodInvoker(HandlerMethodResolver resolver) {
|
private ServletHandlerMethodInvoker(HandlerMethodResolver resolver) {
|
||||||
super(resolver, webBindingInitializer, sessionAttributeStore, parameterNameDiscoverer,
|
super(resolver, webBindingInitializer, sessionAttributeStore, parameterNameDiscoverer,
|
||||||
customArgumentResolvers, getMessageConverters());
|
customArgumentResolvers, messageConverters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -679,7 +714,6 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest) throws Exception {
|
protected Object resolveStandardArgument(Class parameterType, NativeWebRequest webRequest) throws Exception {
|
||||||
|
|
||||||
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
|
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
|
||||||
HttpServletResponse response = (HttpServletResponse) webRequest.getNativeResponse();
|
HttpServletResponse response = (HttpServletResponse) webRequest.getNativeResponse();
|
||||||
|
|
||||||
|
|
@ -717,11 +751,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ModelAndView getModelAndView(Method handlerMethod,
|
public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue,
|
||||||
Class handlerType,
|
ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
|
||||||
Object returnValue,
|
|
||||||
ExtendedModelMap implicitModel,
|
|
||||||
ServletWebRequest webRequest) throws Exception {
|
|
||||||
|
|
||||||
ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
|
ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
|
||||||
if (responseStatusAnn != null) {
|
if (responseStatusAnn != null) {
|
||||||
|
|
@ -792,6 +823,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void handleResponseBody(Object returnValue, ServletWebRequest webRequest)
|
private void handleResponseBody(Object returnValue, ServletWebRequest webRequest)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
|
||||||
HttpInputMessage inputMessage = new ServletServerHttpRequest(webRequest.getRequest());
|
HttpInputMessage inputMessage = new ServletServerHttpRequest(webRequest.getRequest());
|
||||||
List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
|
List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
|
||||||
if (acceptedMediaTypes.isEmpty()) {
|
if (acceptedMediaTypes.isEmpty()) {
|
||||||
|
|
@ -800,8 +832,8 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
HttpOutputMessage outputMessage = new ServletServerHttpResponse(webRequest.getResponse());
|
HttpOutputMessage outputMessage = new ServletServerHttpResponse(webRequest.getResponse());
|
||||||
Class<?> returnValueType = returnValue.getClass();
|
Class<?> returnValueType = returnValue.getClass();
|
||||||
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
|
List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
|
||||||
if (getMessageConverters() != null) {
|
if (messageConverters != null) {
|
||||||
for (HttpMessageConverter messageConverter : getMessageConverters()) {
|
for (HttpMessageConverter messageConverter : messageConverters) {
|
||||||
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
|
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
|
||||||
for (MediaType acceptedMediaType : acceptedMediaTypes) {
|
for (MediaType acceptedMediaType : acceptedMediaTypes) {
|
||||||
if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
|
if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
|
||||||
|
|
@ -816,6 +848,10 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holder for request mapping metadata. Allows for finding a best matching candidate.
|
||||||
|
*/
|
||||||
static class RequestMappingInfo {
|
static class RequestMappingInfo {
|
||||||
|
|
||||||
String[] paths = new String[0];
|
String[] paths = new String[0];
|
||||||
|
|
@ -852,6 +888,7 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Comparator capable of sorting {@link RequestMappingInfo}s (RHIs) so that sorting a list with this comparator will
|
* Comparator capable of sorting {@link RequestMappingInfo}s (RHIs) so that sorting a list with this comparator will
|
||||||
* result in: <ul> <li>RHIs with {@linkplain RequestMappingInfo#matchedPaths better matched paths} take prescedence
|
* result in: <ul> <li>RHIs with {@linkplain RequestMappingInfo#matchedPaths better matched paths} take prescedence
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue