Replace MvcUrls with MvcUriComponentsBuilder
Issue: SPR-8826
This commit is contained in:
@@ -19,6 +19,8 @@ package org.springframework.web.servlet.config;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
@@ -28,6 +30,7 @@ import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.format.support.FormattingConversionServiceFactoryBean;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -52,6 +55,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.method.support.CompositeUriComponentsContributor;
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
@@ -67,6 +71,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ServletWebArgumentResolverAdapter;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
@@ -207,12 +212,12 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
|
||||
String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);
|
||||
|
||||
String mvcUrlsName = "mvcUrls";
|
||||
RootBeanDefinition mvcUrlsDef = new RootBeanDefinition(DefaultMvcUrlsFactoryBean.class);
|
||||
mvcUrlsDef.setSource(source);
|
||||
mvcUrlsDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
|
||||
mvcUrlsDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
|
||||
parserContext.getReaderContext().getRegistry().registerBeanDefinition(mvcUrlsName, mvcUrlsDef);
|
||||
String uriCompContribName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
|
||||
RootBeanDefinition uriCompContribDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
|
||||
uriCompContribDef.setSource(source);
|
||||
uriCompContribDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
|
||||
uriCompContribDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
|
||||
parserContext.getReaderContext().getRegistry().registerBeanDefinition(uriCompContribName, uriCompContribDef);
|
||||
|
||||
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
|
||||
csInterceptorDef.setSource(source);
|
||||
@@ -249,7 +254,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, methodMappingName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, handlerAdapterName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(mvcUrlsDef, mvcUrlsName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
|
||||
@@ -484,4 +489,50 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A FactoryBean for a CompositeUriComponentsContributor that obtains the
|
||||
* HandlerMethodArgumentResolver's configured in RequestMappingHandlerAdapter
|
||||
* after it is fully initialized.
|
||||
*/
|
||||
private static class CompositeUriComponentsContributorFactoryBean
|
||||
implements InitializingBean, FactoryBean<CompositeUriComponentsContributor> {
|
||||
|
||||
private RequestMappingHandlerAdapter handlerAdapter;
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
private CompositeUriComponentsContributor uriComponentsContributor;
|
||||
|
||||
|
||||
public void setHandlerAdapter(RequestMappingHandlerAdapter handlerAdapter) {
|
||||
this.handlerAdapter = handlerAdapter;
|
||||
}
|
||||
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.uriComponentsContributor = new CompositeUriComponentsContributor(
|
||||
this.handlerAdapter.getArgumentResolvers(), this.conversionService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompositeUriComponentsContributor getObject() throws Exception {
|
||||
return this.uriComponentsContributor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return CompositeUriComponentsContributor.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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.web.servlet.config;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.UriComponentsContributor;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultMvcUrls;
|
||||
import org.springframework.web.servlet.mvc.support.MvcUrls;
|
||||
|
||||
|
||||
/**
|
||||
* A factory bean for creating an instance of {@link MvcUrls} that discovers
|
||||
* {@link UriComponentsContributor}s by obtaining the
|
||||
* {@link HandlerMethodArgumentResolver}s configured in a
|
||||
* {@link RequestMappingHandlerAdapter}.
|
||||
* <p>
|
||||
* This is mainly provided as a convenience in XML configuration. Otherwise call the
|
||||
* constructors of {@link DefaultMvcUrls} directly. Also note the MVC Java config and
|
||||
* XML namespace already create an instance of {@link MvcUrls} and when using either
|
||||
* of them this {@code FactoryBean} is not needed.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class DefaultMvcUrlsFactoryBean implements InitializingBean, FactoryBean<MvcUrls> {
|
||||
|
||||
private RequestMappingHandlerAdapter handlerAdapter;
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
private MvcUrls mvcUrls;
|
||||
|
||||
|
||||
/**
|
||||
* Provide a {@link RequestMappingHandlerAdapter} from which to obtain
|
||||
* the list of configured {@link HandlerMethodArgumentResolver}s. This
|
||||
* is provided for ease of configuration in XML.
|
||||
*/
|
||||
public void setHandlerAdapter(RequestMappingHandlerAdapter handlerAdapter) {
|
||||
this.handlerAdapter = handlerAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the {@link ConversionService} instance that {@link MvcUrls} should
|
||||
* use to format Object values being added to a URI.
|
||||
*/
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.mvcUrls = new DefaultMvcUrls(this.handlerAdapter.getArgumentResolvers(), this.conversionService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MvcUrls getObject() throws Exception {
|
||||
return this.mvcUrls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return MvcUrls.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -61,6 +61,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.context.ServletContextAware;
|
||||
import org.springframework.web.method.support.CompositeUriComponentsContributor;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
@@ -78,8 +79,6 @@ import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExc
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.mvc.support.DefaultMvcUrls;
|
||||
import org.springframework.web.servlet.mvc.support.MvcUrls;
|
||||
|
||||
/**
|
||||
* This is the main class providing the configuration behind the MVC Java config.
|
||||
@@ -566,12 +565,13 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance of {@link MvcUrls} for injection into controllers to create
|
||||
* URLs by referencing controllers and controller methods.
|
||||
* Return an instance of {@link CompositeUriComponentsContributor} for use with
|
||||
* {@link org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder}.
|
||||
*/
|
||||
@Bean
|
||||
public MvcUrls mvcUrls() {
|
||||
return new DefaultMvcUrls(requestMappingHandlerAdapter().getArgumentResolvers(), mvcConversionService());
|
||||
public CompositeUriComponentsContributor mvcUriComponentsContributor() {
|
||||
return new CompositeUriComponentsContributor(
|
||||
requestMappingHandlerAdapter().getArgumentResolvers(), mvcConversionService());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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.web.servlet.mvc.method.annotation;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.target.EmptyTargetSource;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cglib.proxy.Callback;
|
||||
import org.springframework.cglib.proxy.Enhancer;
|
||||
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.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.objenesis.ObjenesisStd;
|
||||
import org.springframework.util.*;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
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.context.support.WebApplicationContextUtils;
|
||||
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.CompositeUriComponentsContributor;
|
||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A UriComponentsBuilder that helps to build URIs to Spring MVC controllers and methods from their
|
||||
* request mappings.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Rossen Stoyanchev
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
|
||||
public class MvcUriComponentsBuilder extends UriComponentsBuilder {
|
||||
|
||||
/**
|
||||
* Well-known name for the {@link CompositeUriComponentsContributor} object in the bean factory.
|
||||
*/
|
||||
public static final String MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME = "mvcUriComponentsContributor";
|
||||
|
||||
|
||||
private static final CompositeUriComponentsContributor defaultUriComponentsContributor;
|
||||
|
||||
private static final PathMatcher pathMatcher = new AntPathMatcher();
|
||||
|
||||
private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
|
||||
private final static ObjenesisStd objenesis = new ObjenesisStd(true);
|
||||
|
||||
private static Log logger = LogFactory.getLog(MvcUriComponentsBuilder.class);
|
||||
|
||||
|
||||
static {
|
||||
defaultUriComponentsContributor = new CompositeUriComponentsContributor(
|
||||
Arrays.asList(
|
||||
new PathVariableMethodArgumentResolver(),
|
||||
new RequestParamMethodArgumentResolver(null, false)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@link UriComponentsBuilder} by pointing to a controller class. The
|
||||
* resulting builder contains the current request information up to and including
|
||||
* the Servlet mapping plus any type-level request mapping. If the controller
|
||||
* contains multiple mappings, the first one is used.
|
||||
*
|
||||
* @param controllerType the controller to create a URI for
|
||||
*
|
||||
* @return a UriComponentsBuilder instance
|
||||
*/
|
||||
public static UriComponentsBuilder fromController(Class<?> controllerType) {
|
||||
String mapping = getTypeRequestMapping(controllerType);
|
||||
return ServletUriComponentsBuilder.fromCurrentServletMapping().path(mapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link UriComponentsBuilder} by pointing to a controller method and
|
||||
* providing method argument values. The method is matched based on the provided
|
||||
* method name and the number of argument values. If that results in a clash
|
||||
* (i.e. overloaded methods with the same number of parameters), use
|
||||
* {@link #fromMethod(java.lang.reflect.Method, Object...)} instead.
|
||||
* <p>
|
||||
* The argument values are used to prepare the URI for example expanding
|
||||
* path variables, or adding query parameters. Any other arguments not
|
||||
* relevant to the URI can be provided as {@literal null} and will be ignored.
|
||||
* <p>
|
||||
* Additional (custom) argument types can be supported through an implementation
|
||||
* of {@link org.springframework.web.method.support.UriComponentsContributor}.
|
||||
*
|
||||
* @param controllerType the target controller type
|
||||
* @param methodName the target method name
|
||||
* @param argumentValues argument values matching to method parameters
|
||||
*
|
||||
* @return a UriComponentsBuilder instance
|
||||
*/
|
||||
public static UriComponentsBuilder fromMethodName(Class<?> controllerType,
|
||||
String methodName, Object... argumentValues) {
|
||||
|
||||
Method match = null;
|
||||
for (Method method : controllerType.getDeclaredMethods()) {
|
||||
if ((method.getParameterCount() == argumentValues.length) && method.getName().equals(methodName)) {
|
||||
if (match != null) {
|
||||
throw new IllegalStateException("Found two methods named '" + methodName
|
||||
+ "' having " + argumentValues + " arguments, controller "
|
||||
+ controllerType.getName());
|
||||
}
|
||||
match = method;
|
||||
}
|
||||
}
|
||||
if (match == null) {
|
||||
throw new IllegalArgumentException("No method '" + methodName + "' with "
|
||||
+ argumentValues.length + " parameters found in " + controllerType.getName());
|
||||
}
|
||||
return fromMethod(match, argumentValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link UriComponentsBuilder} by pointing to a controller method and
|
||||
* providing method argument values. The method argument values are used to
|
||||
* prepare the URI for example expanding path variables, or adding request
|
||||
* parameters. Any other arguments not relevant to the URL can be provided as
|
||||
* {@literal null} and will be ignored.
|
||||
* <p>
|
||||
* Additional (custom) argument types can be supported through an implementation
|
||||
* of {@link org.springframework.web.method.support.UriComponentsContributor}.
|
||||
*
|
||||
* @param method the target controller method
|
||||
* @param argumentValues argument values matching to method parameters
|
||||
*
|
||||
* @return a UriComponentsBuilder instance
|
||||
*/
|
||||
public static UriComponentsBuilder fromMethod(Method method, Object... argumentValues) {
|
||||
|
||||
UriComponentsBuilder builder = ServletUriComponentsBuilder.newInstance().path(getMethodRequestMapping(method));
|
||||
UriComponents uriComponents = applyContributors(builder, method, argumentValues);
|
||||
|
||||
String typePath = getTypeRequestMapping(method.getDeclaringClass());
|
||||
String methodPath = uriComponents.getPath();
|
||||
String path = pathMatcher.combine(typePath, methodPath);
|
||||
|
||||
return ServletUriComponentsBuilder.fromCurrentServletMapping().path(
|
||||
path).queryParams(uriComponents.getQueryParams());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link UriComponents} by invoking a method on a "mock" controller, similar
|
||||
* to how test frameworks provide mock objects and record method invocations.
|
||||
* <p>
|
||||
* For example given this controller:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @RequestMapping("/people/{id}/addresses")
|
||||
* class AddressController {
|
||||
*
|
||||
* @RequestMapping("/{country}")
|
||||
* public HttpEntity<Void> getAddressesForCountry(@PathVariable String country) { … }
|
||||
*
|
||||
* @RequestMapping(value="/", method=RequestMethod.POST)
|
||||
* public void addAddress(Address address) { … }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* A "mock" controller can be used as follows:
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* // Inline style with static import of MvcUriComponentsBuilder.mock
|
||||
*
|
||||
* MvcUriComponentsBuilder.fromLastCall(
|
||||
* mock(CustomerController.class).showAddresses("US")).buildAndExpand(1);
|
||||
*
|
||||
* // Longer style for preparing multiple URIs and for void controller methods
|
||||
*
|
||||
* CustomerController controller = MvcUriComponentsBuilder.mock(CustomController.class);
|
||||
* controller.addAddress(null);
|
||||
*
|
||||
* MvcUriComponentsBuilder.fromLastCall(controller);
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* The above supports {@codce @PathVariable} and {@code @RequestParam} method parameters.
|
||||
* Any other arguments can be provided as {@literal null} and will be ignored.
|
||||
* <p>
|
||||
* Additional (custom) argument types can be supported through an implementation
|
||||
* of {@link org.springframework.web.method.support.UriComponentsContributor}.
|
||||
*
|
||||
* @param methodInvocationInfo either the value returned from a "mock" controller
|
||||
* invocation or the "mock" controller itself after an invocation
|
||||
*
|
||||
* @return a UriComponents instance
|
||||
*/
|
||||
public static UriComponentsBuilder fromLastCall(Object methodInvocationInfo) {
|
||||
|
||||
Assert.isInstanceOf(MethodInvocationInfo.class, methodInvocationInfo);
|
||||
MethodInvocationInfo info = (MethodInvocationInfo) methodInvocationInfo;
|
||||
|
||||
Method method = info.getControllerMethod();
|
||||
Object[] argumentValues = info.getArgumentValues();
|
||||
|
||||
UriComponentsBuilder builder = ServletUriComponentsBuilder.newInstance().path(getMethodRequestMapping(method));
|
||||
UriComponents uriComponents = applyContributors(builder, method, argumentValues);
|
||||
|
||||
String typeMapping = getTypeRequestMapping(method.getDeclaringClass());
|
||||
String methodMapping = uriComponents.getPath();
|
||||
String path = pathMatcher.combine(typeMapping, methodMapping);
|
||||
|
||||
return ServletUriComponentsBuilder.fromCurrentServletMapping().path(
|
||||
path).queryParams(uriComponents.getQueryParams());
|
||||
}
|
||||
|
||||
|
||||
private static String getTypeRequestMapping(Class<?> controllerType) {
|
||||
Assert.notNull(controllerType, "'controllerType' must not be null");
|
||||
RequestMapping annot = AnnotationUtils.findAnnotation(controllerType, RequestMapping.class);
|
||||
if ((annot == null) || ObjectUtils.isEmpty(annot.value()) || StringUtils.isEmpty(annot.value()[0])) {
|
||||
return "/";
|
||||
}
|
||||
if (annot.value().length > 1) {
|
||||
logger.warn("Multiple mappings on controller " + controllerType.getName() + ", using the first one");
|
||||
}
|
||||
return annot.value()[0];
|
||||
}
|
||||
|
||||
private static String getMethodRequestMapping(Method method) {
|
||||
RequestMapping annot = AnnotationUtils.findAnnotation(method, RequestMapping.class);
|
||||
Assert.notNull(annot, "No @RequestMapping on: " + method.toGenericString());
|
||||
if (ObjectUtils.isEmpty(annot.value()) || StringUtils.isEmpty(annot.value()[0])) {
|
||||
return "/";
|
||||
}
|
||||
if (annot.value().length > 1) {
|
||||
logger.debug("Multiple mappings on method " + method.toGenericString() + ", using first one");
|
||||
}
|
||||
return annot.value()[0];
|
||||
}
|
||||
|
||||
private static UriComponents applyContributors(UriComponentsBuilder builder, Method method, Object[] args) {
|
||||
|
||||
CompositeUriComponentsContributor contributor = getConfiguredUriComponentsContributor();
|
||||
if (contributor == null) {
|
||||
logger.debug("Using default CompositeUriComponentsContributor");
|
||||
contributor = defaultUriComponentsContributor;
|
||||
}
|
||||
|
||||
int paramCount = method.getParameterCount();
|
||||
int argCount = args.length;
|
||||
|
||||
Assert.isTrue(paramCount == argCount, "Number of method parameters " + paramCount +
|
||||
" does not match number of argument values " + argCount);
|
||||
|
||||
Map<String, Object> uriVars = new HashMap<String, Object>();
|
||||
|
||||
for (int i=0; i < paramCount; i++) {
|
||||
MethodParameter param = new MethodParameter(method, i);
|
||||
param.initParameterNameDiscovery(parameterNameDiscoverer);
|
||||
contributor.contributeMethodArgument(param, args[i], builder, uriVars);
|
||||
}
|
||||
|
||||
return builder.buildAndExpand(uriVars);
|
||||
}
|
||||
|
||||
protected static CompositeUriComponentsContributor getConfiguredUriComponentsContributor() {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
if (requestAttributes == null) {
|
||||
logger.debug("No request bound to the current thread: is DispatcherSerlvet used?");
|
||||
return null;
|
||||
}
|
||||
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
|
||||
if (request == null) {
|
||||
logger.debug("Request bound to current thread is not an HttpServletRequest");
|
||||
return null;
|
||||
}
|
||||
ServletContext servletContext = request.getServletContext();
|
||||
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(servletContext);
|
||||
if (wac == null) {
|
||||
logger.debug("No WebApplicationContext found: no ContextLoaderListener registered?");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String beanName = MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
|
||||
return wac.getBean(beanName, CompositeUriComponentsContributor.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("No CompositeUriComponentsContributor bean with name '"
|
||||
+ MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME + "'");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a "mock" controller instance. When a method on the mock is invoked, the
|
||||
* supplied argument values are remembered and the result can then be used to
|
||||
* prepare a UriComponents through the factory method {@link #fromLastCall(Object)}.
|
||||
* <p>
|
||||
* The controller can be invoked more than once. However, only the last invocation
|
||||
* is remembered. This means the same mock controller can be used to create
|
||||
* multiple links in succession, for example:
|
||||
*
|
||||
* <pre>
|
||||
* CustomerController controller = MvcUriComponentsBuilder.mock(CustomController.class);
|
||||
*
|
||||
* MvcUriComponentsBuilder.fromLastCall(controller.getFoo(1)).build();
|
||||
* MvcUriComponentsBuilder.fromLastCall(controller.getFoo(2)).build();
|
||||
*
|
||||
* MvcUriComponentsBuilder.fromLastCall(controller.getBar(1)).build();
|
||||
* MvcUriComponentsBuilder.fromLastCall(controller.getBar(2)).build();
|
||||
* </pre>
|
||||
*
|
||||
* If a controller method returns void, use the following style:
|
||||
*
|
||||
* <pre>
|
||||
* CustomerController controller = MvcUriComponentsBuilder.mock(CustomController.class);
|
||||
*
|
||||
* controller.handleFoo(1);
|
||||
* MvcUriComponentsBuilder.fromLastCall(controller).build();
|
||||
*
|
||||
* controller.handleFoo(2)
|
||||
* MvcUriComponentsBuilder.fromLastCall(controller).build();
|
||||
* </pre>
|
||||
*
|
||||
* @param controllerType the type of controller to mock
|
||||
*/
|
||||
public static <T> T mock(Class<T> controllerType) {
|
||||
Assert.notNull(controllerType, "'controllerType' must not be null");
|
||||
return initProxy(controllerType, new ControllerMethodInvocationInterceptor());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T initProxy(Class<?> type, ControllerMethodInvocationInterceptor interceptor) {
|
||||
|
||||
if (type.isInterface()) {
|
||||
ProxyFactory factory = new ProxyFactory(EmptyTargetSource.INSTANCE);
|
||||
factory.addInterface(type);
|
||||
factory.addInterface(MethodInvocationInfo.class);
|
||||
factory.addAdvice(interceptor);
|
||||
return (T) factory.getProxy();
|
||||
}
|
||||
else {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(type);
|
||||
enhancer.setInterfaces(new Class<?>[]{MethodInvocationInfo.class});
|
||||
enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);
|
||||
|
||||
Factory factory = (Factory) objenesis.newInstance(enhancer.createClass());
|
||||
factory.setCallbacks(new Callback[] { interceptor });
|
||||
return (T) factory;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ControllerMethodInvocationInterceptor
|
||||
implements org.springframework.cglib.proxy.MethodInterceptor, MethodInterceptor {
|
||||
|
||||
private static final Method getControllerMethod =
|
||||
ReflectionUtils.findMethod(MethodInvocationInfo.class, "getControllerMethod");
|
||||
|
||||
private static final Method getArgumentValues =
|
||||
ReflectionUtils.findMethod(MethodInvocationInfo.class, "getArgumentValues");
|
||||
|
||||
|
||||
private Method controllerMethod;
|
||||
|
||||
private Object[] argumentValues;
|
||||
|
||||
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
|
||||
|
||||
if (getControllerMethod.equals(method)) {
|
||||
return this.controllerMethod;
|
||||
}
|
||||
else if (getArgumentValues.equals(method)) {
|
||||
return this.argumentValues;
|
||||
}
|
||||
else if (ReflectionUtils.isObjectMethod(method)) {
|
||||
return ReflectionUtils.invokeMethod(method, obj, args);
|
||||
}
|
||||
else {
|
||||
this.controllerMethod = method;
|
||||
this.argumentValues = args;
|
||||
|
||||
Class<?> returnType = method.getReturnType();
|
||||
return void.class.equals(returnType) ? null : returnType.cast(initProxy(returnType, this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(org.aopalliance.intercept.MethodInvocation inv) throws Throwable {
|
||||
return intercept(inv.getThis(), inv.getMethod(), inv.getArguments(), null);
|
||||
}
|
||||
}
|
||||
|
||||
public interface MethodInvocationInfo {
|
||||
|
||||
Method getControllerMethod();
|
||||
|
||||
Object[] getArgumentValues();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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.web.servlet.mvc.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.UriComponentsContributor;
|
||||
import org.springframework.web.servlet.mvc.support.MvcUrlUtils.ControllerMethodValues;
|
||||
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
|
||||
/**
|
||||
* A default {@link MvcUrls} implementation.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Rossen Stoyanchev
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class DefaultMvcUrls implements MvcUrls {
|
||||
|
||||
private static final ParameterNameDiscoverer parameterNameDiscoverer =
|
||||
new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
|
||||
private final List<UriComponentsContributor> contributors = new ArrayList<UriComponentsContributor>();
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance providing a collection of {@link UriComponentsContributor}s or
|
||||
* {@link HandlerMethodArgumentResolver}s. Since both of these tend to be implemented
|
||||
* by the same class, the most convenient option is to obtain the configured
|
||||
* {@code HandlerMethodArgumentResolvers} in the {@code RequestMappingHandlerAdapter}
|
||||
* and provide that to this constructor.
|
||||
* @param uriComponentsContributors a collection of {@link UriComponentsContributor}
|
||||
* or {@link HandlerMethodArgumentResolver}s.
|
||||
*/
|
||||
public DefaultMvcUrls(Collection<?> uriComponentsContributors) {
|
||||
this(uriComponentsContributors, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance providing a collection of {@link UriComponentsContributor}s or
|
||||
* {@link HandlerMethodArgumentResolver}s. Since both of these tend to be implemented
|
||||
* by the same class, the most convenient option is to obtain the configured
|
||||
* {@code HandlerMethodArgumentResolvers} in the {@code RequestMappingHandlerAdapter}
|
||||
* and provide that to this constructor.
|
||||
*
|
||||
* <p>If the {@link ConversionService} argument is {@code null},
|
||||
* {@link DefaultFormattingConversionService} will be used by default.
|
||||
* @param uriComponentsContributors a collection of {@link UriComponentsContributor}
|
||||
* or {@link HandlerMethodArgumentResolver}s.
|
||||
* @param conversionService a ConversionService to use when method argument values
|
||||
* need to be formatted as Strings before being added to the URI
|
||||
*/
|
||||
public DefaultMvcUrls(Collection<?> uriComponentsContributors, ConversionService conversionService) {
|
||||
Assert.notNull(uriComponentsContributors, "'uriComponentsContributors' must not be null");
|
||||
for (Object contributor : uriComponentsContributors) {
|
||||
if (contributor instanceof UriComponentsContributor) {
|
||||
this.contributors.add((UriComponentsContributor) contributor);
|
||||
}
|
||||
}
|
||||
this.conversionService = (conversionService != null) ?
|
||||
conversionService : new DefaultFormattingConversionService();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UriComponentsBuilder linkToController(Class<?> controllerClass) {
|
||||
String mapping = MvcUrlUtils.getTypeLevelMapping(controllerClass);
|
||||
return ServletUriComponentsBuilder.fromCurrentServletMapping().path(mapping);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UriComponents linkToMethod(Method method, Object... argumentValues) {
|
||||
String mapping = MvcUrlUtils.getMethodMapping(method);
|
||||
UriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentServletMapping().path(mapping);
|
||||
Map<String, Object> uriVars = new HashMap<String, Object>();
|
||||
return applyContributers(builder, method, argumentValues, uriVars);
|
||||
}
|
||||
|
||||
private UriComponents applyContributers(UriComponentsBuilder builder, Method method,
|
||||
Object[] argumentValues, Map<String, Object> uriVars) {
|
||||
if (this.contributors.isEmpty()) {
|
||||
return builder.buildAndExpand(uriVars);
|
||||
}
|
||||
|
||||
int paramCount = method.getParameters().length;
|
||||
int argCount = argumentValues.length;
|
||||
Assert.isTrue(paramCount == argCount, "Number of method parameters " + paramCount +
|
||||
" does not match number of argument values " + argCount);
|
||||
|
||||
for (int i=0; i < paramCount; i++) {
|
||||
MethodParameter param = new MethodParameter(method, i);
|
||||
param.initParameterNameDiscovery(parameterNameDiscoverer);
|
||||
for (UriComponentsContributor c : this.contributors) {
|
||||
if (c.supportsParameter(param)) {
|
||||
c.contributeMethodArgument(param, argumentValues[i], builder, uriVars, this.conversionService);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.buildAndExpand(uriVars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UriComponents linkToMethodOn(Object mockController) {
|
||||
Assert.isInstanceOf(ControllerMethodValues.class, mockController);
|
||||
ControllerMethodValues controllerMethodValues = (ControllerMethodValues) mockController;
|
||||
|
||||
Method method = controllerMethodValues.getControllerMethod();
|
||||
Object[] argumentValues = controllerMethodValues.getArgumentValues();
|
||||
|
||||
Map<String, Object> uriVars = new HashMap<String, Object>();
|
||||
addTypeLevelUriVaris(controllerMethodValues, uriVars);
|
||||
|
||||
String mapping = MvcUrlUtils.getMethodMapping(method);
|
||||
UriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentServletMapping().path(mapping);
|
||||
|
||||
return applyContributers(builder, method, argumentValues, uriVars);
|
||||
}
|
||||
|
||||
private void addTypeLevelUriVaris(ControllerMethodValues info, Map<String, Object> uriVariables) {
|
||||
Object[] values = info.getTypeLevelUriVariables();
|
||||
if (!ObjectUtils.isEmpty(values)) {
|
||||
|
||||
String mapping = MvcUrlUtils.getTypeLevelMapping(info.getControllerMethod().getDeclaringClass());
|
||||
|
||||
List<String> names = new UriTemplate(mapping).getVariableNames();
|
||||
Assert.isTrue(names.size() == values.length, "The provided type-level URI template variables " +
|
||||
Arrays.toString(values) + " do not match the template " + mapping);
|
||||
|
||||
for (int i=0; i < names.size(); i++) {
|
||||
uriVariables.put(names.get(i), values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2013 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.web.servlet.mvc.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.target.EmptyTargetSource;
|
||||
import org.springframework.cglib.proxy.Callback;
|
||||
import org.springframework.cglib.proxy.Enhancer;
|
||||
import org.springframework.cglib.proxy.Factory;
|
||||
import org.springframework.cglib.proxy.MethodProxy;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.objenesis.ObjenesisStd;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
|
||||
/**
|
||||
* Utility methods to support the creation URLs to Spring MVC controllers and controller
|
||||
* methods.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public class MvcUrlUtils {
|
||||
|
||||
private static Log logger = LogFactory.getLog(MvcUrlUtils.class);
|
||||
|
||||
private final static ObjenesisStd OBJENESIS = new ObjenesisStd(true);
|
||||
|
||||
|
||||
/**
|
||||
* Extract the type-level URL mapping or return an empty String. If multiple mappings
|
||||
* are found, the first one is used.
|
||||
*/
|
||||
public static String getTypeLevelMapping(Class<?> controllerType) {
|
||||
Assert.notNull(controllerType, "'controllerType' must not be null");
|
||||
RequestMapping annot = AnnotationUtils.findAnnotation(controllerType, RequestMapping.class);
|
||||
if ((annot == null) || ObjectUtils.isEmpty(annot.value())) {
|
||||
return "/";
|
||||
}
|
||||
if (annot.value().length > 1) {
|
||||
logger.warn("Multiple class level mappings on " + controllerType.getName() + ", using the first one");
|
||||
}
|
||||
return annot.value()[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the mapping from the given controller method, including both type and
|
||||
* method-level mappings. If multiple mappings are found, the first one is used.
|
||||
*/
|
||||
public static String getMethodMapping(Method method) {
|
||||
RequestMapping methodAnnot = AnnotationUtils.findAnnotation(method, RequestMapping.class);
|
||||
Assert.notNull(methodAnnot, "No mappings on " + method.toGenericString());
|
||||
PatternsRequestCondition condition = new PatternsRequestCondition(methodAnnot.value());
|
||||
|
||||
RequestMapping typeAnnot = AnnotationUtils.findAnnotation(method.getDeclaringClass(), RequestMapping.class);
|
||||
if (typeAnnot != null) {
|
||||
condition = new PatternsRequestCondition(typeAnnot.value()).combine(condition);
|
||||
}
|
||||
|
||||
Set<String> patterns = condition.getPatterns();
|
||||
if (patterns.size() > 1) {
|
||||
logger.warn("Multiple mappings on " + method.toGenericString() + ", using the first one");
|
||||
}
|
||||
|
||||
return (patterns.size() == 0) ? "/" : patterns.iterator().next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a "mock" controller instance. When a controller method is invoked, the
|
||||
* invoked method and argument values are remembered, and a "mock" value is returned
|
||||
* so it can be used to help prepare a {@link UriComponents} through
|
||||
* {@link MvcUrls#linkToMethodOn(Object)}.
|
||||
* @param controllerType the type of controller to mock, must not be {@literal null}.
|
||||
* @param typeLevelUriVariables URI variables to expand into the type-level mapping
|
||||
* @return the created controller instance
|
||||
*/
|
||||
public static <T> T controller(Class<T> controllerType, Object... typeLevelUriVariables) {
|
||||
Assert.notNull(controllerType, "'type' must not be null");
|
||||
return initProxy(controllerType, new ControllerMethodInvocationInterceptor(typeLevelUriVariables));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T initProxy(Class<?> type, ControllerMethodInvocationInterceptor interceptor) {
|
||||
if (type.isInterface()) {
|
||||
ProxyFactory factory = new ProxyFactory(EmptyTargetSource.INSTANCE);
|
||||
factory.addInterface(type);
|
||||
factory.addInterface(ControllerMethodValues.class);
|
||||
factory.addAdvice(interceptor);
|
||||
return (T) factory.getProxy();
|
||||
}
|
||||
else {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setSuperclass(type);
|
||||
enhancer.setInterfaces(new Class<?>[] { ControllerMethodValues.class });
|
||||
enhancer.setCallbackType(org.springframework.cglib.proxy.MethodInterceptor.class);
|
||||
|
||||
Factory factory = (Factory) OBJENESIS.newInstance(enhancer.createClass());
|
||||
factory.setCallbacks(new Callback[] { interceptor });
|
||||
return (T) factory;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ControllerMethodInvocationInterceptor
|
||||
implements org.springframework.cglib.proxy.MethodInterceptor, MethodInterceptor {
|
||||
|
||||
private static final Method getTypeLevelUriVariables =
|
||||
ReflectionUtils.findMethod(ControllerMethodValues.class, "getTypeLevelUriVariables");
|
||||
|
||||
private static final Method getControllerMethod =
|
||||
ReflectionUtils.findMethod(ControllerMethodValues.class, "getControllerMethod");
|
||||
|
||||
private static final Method getArgumentValues =
|
||||
ReflectionUtils.findMethod(ControllerMethodValues.class, "getArgumentValues");
|
||||
|
||||
|
||||
private final Object[] typeLevelUriVariables;
|
||||
|
||||
private Method controllerMethod;
|
||||
|
||||
private Object[] argumentValues;
|
||||
|
||||
|
||||
public ControllerMethodInvocationInterceptor(Object... typeLevelUriVariables) {
|
||||
this.typeLevelUriVariables = typeLevelUriVariables.clone();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) {
|
||||
|
||||
if (getTypeLevelUriVariables.equals(method)) {
|
||||
return this.typeLevelUriVariables;
|
||||
}
|
||||
else if (getControllerMethod.equals(method)) {
|
||||
return this.controllerMethod;
|
||||
}
|
||||
else if (getArgumentValues.equals(method)) {
|
||||
return this.argumentValues;
|
||||
}
|
||||
else if (ReflectionUtils.isObjectMethod(method)) {
|
||||
return ReflectionUtils.invokeMethod(method, obj, args);
|
||||
}
|
||||
else {
|
||||
this.controllerMethod = method;
|
||||
this.argumentValues = args;
|
||||
|
||||
Class<?> returnType = method.getReturnType();
|
||||
return void.class.equals(returnType) ? null : returnType.cast(initProxy(returnType, this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(org.aopalliance.intercept.MethodInvocation inv) throws Throwable {
|
||||
return intercept(inv.getThis(), inv.getMethod(), inv.getArguments(), null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides information about a controller method that can be used to prepare a URL
|
||||
* including type-level URI template variables, a method reference, and argument
|
||||
* values collected through the invocation of a "mock" controller.
|
||||
* <p>
|
||||
* Instances of this interface are returned from
|
||||
* {@link MvcUrlUtils#controller(Class, Object...) controller(Class, Object...)} and
|
||||
* are needed for {@link MvcUrls#linkToMethodOn(Object)}.
|
||||
*/
|
||||
public interface ControllerMethodValues {
|
||||
|
||||
Object[] getTypeLevelUriVariables();
|
||||
|
||||
Method getControllerMethod();
|
||||
|
||||
Object[] getArgumentValues();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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.web.servlet.mvc.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.method.support.UriComponentsContributor;
|
||||
import org.springframework.web.servlet.config.DefaultMvcUrlsFactoryBean;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* A contract for creating URLs by referencing Spring MVC controllers and methods.
|
||||
*
|
||||
* <p>The MVC Java config and the MVC namespace automatically create an instance of this
|
||||
* contract for use in controllers and anywhere else during the processing of a request.
|
||||
* The best way for access it is to have it autowired, or otherwise injected either by
|
||||
* type or also qualified by name ("mvcUrls") if necessary.
|
||||
*
|
||||
* <p>If not using either option, with explicit configuration it's easy to create an
|
||||
* instance of {@link DefaultMvcUrls} in Java config or in XML configuration, use
|
||||
* {@link DefaultMvcUrlsFactoryBean}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*/
|
||||
public interface MvcUrls {
|
||||
|
||||
/**
|
||||
* Creates a new {@link UriComponentsBuilder} by pointing to a controller class. The
|
||||
* resulting builder contains all the current request information up to and including
|
||||
* the Servlet mapping as well as the portion of the path matching to the controller
|
||||
* level request mapping. If the controller contains multiple mappings, the
|
||||
* {@link DefaultMvcUrls} will use the first one.
|
||||
* @param controllerType the controller type to create a URL to
|
||||
* @return a builder that can be used to further build the {@link UriComponents}.
|
||||
*/
|
||||
UriComponentsBuilder linkToController(Class<?> controllerType);
|
||||
|
||||
/**
|
||||
* Create a {@link UriComponents} by pointing to a controller method along with method
|
||||
* argument values.
|
||||
*
|
||||
* <p>Type and method-level mappings of the controller method are extracted and the
|
||||
* resulting {@link UriComponents} is further enriched with method argument values from
|
||||
* {@link PathVariable} and {@link RequestParam} parameters. Any other arguments not
|
||||
* relevant to the building of the URL can be provided as {@literal null} and will be
|
||||
* ignored. Support for additional custom arguments can be added through a
|
||||
* {@link UriComponentsContributor}.
|
||||
* @param method the target controller method
|
||||
* @param argumentValues argument values matching to method parameters
|
||||
* @return UriComponents instance, never {@literal null}
|
||||
*/
|
||||
UriComponents linkToMethod(Method method, Object... argumentValues);
|
||||
|
||||
/**
|
||||
* Create a {@link UriComponents} by invoking a method on a "mock" controller similar
|
||||
* to how test frameworks provide mock objects and record method invocations. The
|
||||
* static method {@link MvcUrlUtils#controller(Class, Object...)} can be used to
|
||||
* create a "mock" controller:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @RequestMapping("/people/{id}/addresses")
|
||||
* class AddressController {
|
||||
*
|
||||
* @RequestMapping("/{country}")
|
||||
* public HttpEntity<Void> getAddressesForCountry(@PathVariable String country) { ... }
|
||||
*
|
||||
* @RequestMapping(value="/", method=RequestMethod.POST)
|
||||
* public void addAddress(Address address) { ... }
|
||||
* }
|
||||
*
|
||||
* // short-hand style with static import of MvcUrlUtils.controller
|
||||
*
|
||||
* mvcUrls.linkToMethodOn(controller(CustomerController.class, 1).showAddresses("US"));
|
||||
*
|
||||
* // longer style, required for void controller methods
|
||||
*
|
||||
* CustomerController controller = MvcUrlUtils.controller(CustomController.class, 1);
|
||||
* controller.addAddress(null);
|
||||
*
|
||||
* mvcUrls.linkToMethodOn(controller);
|
||||
* </pre>
|
||||
*
|
||||
* The above mechanism supports {@link PathVariable} and {@link RequestParam} method
|
||||
* arguments. Any other arguments can be provided as {@literal null} and will be
|
||||
* ignored. Additional custom arguments can be added through an implementation of
|
||||
* {@link UriComponentsContributor}.
|
||||
* @param mockController created via {@link MvcUrlUtils#controller(Class, Object...)}
|
||||
* @return UriComponents instance, never {@literal null}
|
||||
*/
|
||||
UriComponents linkToMethodOn(Object mockController);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user