@RequestParam and co support placeholders and expressions in their defaultValue attributes (SPR-5922); @Value expressions supported as MVC handler method arguments as well (against request scope)
This commit is contained in:
parent
7835e66abb
commit
bb70c9a4c4
|
|
@ -25,8 +25,11 @@ import java.lang.annotation.Target;
|
|||
* Annotation at the field or method/constructor parameter level
|
||||
* that indicates a default value expression for the affected argument.
|
||||
*
|
||||
* <p>This is typically used for assigning default field values
|
||||
* with "#{systemProperties.myProp}" style expressions.
|
||||
* <p>Typically used for expression-driven dependency injection. Also supported
|
||||
* for dynamic resolution of handler method parameters, e.g. in Spring MVC.
|
||||
*
|
||||
* <p>A common use case is to assign default field values using
|
||||
* "#{systemProperties.myProp}" style expressions.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
|
|
|
|||
|
|
@ -53,11 +53,15 @@ import javax.portlet.WindowState;
|
|||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.BeanExpressionContext;
|
||||
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.style.StylerUtils;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.ui.ExtendedModelMap;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.Assert;
|
||||
|
|
@ -78,6 +82,7 @@ import org.springframework.web.bind.support.SessionAttributeStore;
|
|||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestScope;
|
||||
import org.springframework.web.portlet.HandlerAdapter;
|
||||
import org.springframework.web.portlet.ModelAndView;
|
||||
import org.springframework.web.portlet.bind.MissingPortletRequestParameterException;
|
||||
|
|
@ -112,7 +117,7 @@ import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
|
|||
* @see #setWebBindingInitializer
|
||||
* @see #setSessionAttributeStore
|
||||
*/
|
||||
public class AnnotationMethodHandlerAdapter extends PortletContentGenerator implements HandlerAdapter {
|
||||
public class AnnotationMethodHandlerAdapter extends PortletContentGenerator implements HandlerAdapter, BeanFactoryAware {
|
||||
|
||||
private static final String IMPLICIT_MODEL_ATTRIBUTE = "org.springframework.web.portlet.mvc.ImplicitModel";
|
||||
|
||||
|
|
@ -131,6 +136,10 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl
|
|||
|
||||
private ModelAndViewResolver[] customModelAndViewResolvers;
|
||||
|
||||
private ConfigurableBeanFactory beanFactory;
|
||||
|
||||
private BeanExpressionContext expressionContext;
|
||||
|
||||
private final Map<Class<?>, PortletHandlerMethodResolver> methodResolverCache =
|
||||
new ConcurrentHashMap<Class<?>, PortletHandlerMethodResolver>();
|
||||
|
||||
|
|
@ -233,6 +242,13 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl
|
|||
this.customModelAndViewResolvers = customModelAndViewResolvers;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
if (beanFactory instanceof ConfigurableBeanFactory) {
|
||||
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
|
||||
this.expressionContext = new BeanExpressionContext(this.beanFactory, new RequestScope());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean supports(Object handler) {
|
||||
return getMethodResolver(handler).hasHandlerMethods();
|
||||
|
|
@ -543,7 +559,7 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl
|
|||
|
||||
public PortletHandlerMethodInvoker(HandlerMethodResolver resolver) {
|
||||
super(resolver, webBindingInitializer, sessionAttributeStore,
|
||||
parameterNameDiscoverer, customArgumentResolvers, new HttpMessageConverter[0]);
|
||||
parameterNameDiscoverer, customArgumentResolvers, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -556,6 +572,19 @@ public class AnnotationMethodHandlerAdapter extends PortletContentGenerator impl
|
|||
throw new PortletSessionRequiredException(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveDefaultValue(String value) {
|
||||
if (beanFactory == null) {
|
||||
return value;
|
||||
}
|
||||
String placeholdersResolved = beanFactory.resolveEmbeddedValue(value);
|
||||
BeanExpressionResolver exprResolver = beanFactory.getBeanExpressionResolver();
|
||||
if (exprResolver == null) {
|
||||
return value;
|
||||
}
|
||||
return exprResolver.evaluate(placeholdersResolved, expressionContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
|
||||
throws Exception {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.BeanExpressionContext;
|
||||
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
|
@ -88,6 +93,7 @@ import org.springframework.web.bind.support.SessionAttributeStore;
|
|||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.RequestScope;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
|
@ -120,18 +126,16 @@ import org.springframework.web.util.WebUtils;
|
|||
* @see #setWebBindingInitializer
|
||||
* @see #setSessionAttributeStore
|
||||
*/
|
||||
public class AnnotationMethodHandlerAdapter extends WebContentGenerator implements HandlerAdapter {
|
||||
public class AnnotationMethodHandlerAdapter extends WebContentGenerator implements HandlerAdapter, BeanFactoryAware {
|
||||
|
||||
/**
|
||||
* Log category to use when no mapped handler is found for a request.
|
||||
*
|
||||
* @see #pageNotFoundLogger
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @see #PAGE_NOT_FOUND_LOG_CATEGORY
|
||||
*/
|
||||
protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
|
||||
|
|
@ -156,13 +160,17 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
|
||||
private ModelAndViewResolver[] customModelAndViewResolvers;
|
||||
|
||||
private final Map<Class<?>, ServletHandlerMethodResolver> methodResolverCache =
|
||||
new ConcurrentHashMap<Class<?>, ServletHandlerMethodResolver>();
|
||||
|
||||
private HttpMessageConverter<?>[] messageConverters =
|
||||
new HttpMessageConverter[] {new ByteArrayHttpMessageConverter(), new StringHttpMessageConverter(),
|
||||
new FormHttpMessageConverter(), new SourceHttpMessageConverter()};
|
||||
|
||||
private ConfigurableBeanFactory beanFactory;
|
||||
|
||||
private BeanExpressionContext expressionContext;
|
||||
|
||||
private final Map<Class<?>, ServletHandlerMethodResolver> methodResolverCache =
|
||||
new ConcurrentHashMap<Class<?>, ServletHandlerMethodResolver>();
|
||||
|
||||
|
||||
public AnnotationMethodHandlerAdapter() {
|
||||
// no restriction of HTTP methods by default
|
||||
|
|
@ -318,6 +326,13 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
this.messageConverters = messageConverters;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
if (beanFactory instanceof ConfigurableBeanFactory) {
|
||||
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
|
||||
this.expressionContext = new BeanExpressionContext(this.beanFactory, new RequestScope());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean supports(Object handler) {
|
||||
return getMethodResolver(handler).hasHandlerMethods();
|
||||
|
|
@ -595,6 +610,19 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator implemen
|
|||
throw new HttpSessionRequiredException(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object resolveDefaultValue(String value) {
|
||||
if (beanFactory == null) {
|
||||
return value;
|
||||
}
|
||||
String placeholdersResolved = beanFactory.resolveEmbeddedValue(value);
|
||||
BeanExpressionResolver exprResolver = beanFactory.getBeanExpressionResolver();
|
||||
if (exprResolver == null) {
|
||||
return value;
|
||||
}
|
||||
return exprResolver.evaluate(placeholdersResolved, expressionContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception {
|
||||
HttpServletRequest servletRequest = (HttpServletRequest) webRequest.getNativeRequest();
|
||||
|
|
|
|||
|
|
@ -51,11 +51,12 @@ import org.junit.Test;
|
|||
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
||||
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.DerivedTestBean;
|
||||
import org.springframework.beans.ITestBean;
|
||||
import org.springframework.beans.TestBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.propertyeditors.CustomDateEditor;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
|
|
@ -186,7 +187,7 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void defaultParamMissing() throws Exception {
|
||||
public void defaultParameters() throws Exception {
|
||||
initServlet(DefaultValueParamController.class);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath.do");
|
||||
|
|
@ -195,6 +196,23 @@ public class ServletAnnotationControllerTests {
|
|||
assertEquals("foo-bar", response.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultExpressionParameters() throws Exception {
|
||||
initServlet(DefaultExpressionValueParamController.class);
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myPath.do");
|
||||
request.setContextPath("/myApp");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
System.setProperty("myHeader", "bar");
|
||||
try {
|
||||
servlet.service(request, response);
|
||||
}
|
||||
finally {
|
||||
System.clearProperty("myHeader");
|
||||
}
|
||||
assertEquals("foo-bar-/myApp", response.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodNotAllowed() throws Exception {
|
||||
initServlet(MethodNotAllowedController.class);
|
||||
|
|
@ -281,13 +299,15 @@ public class ServletAnnotationControllerTests {
|
|||
doTestAdaptedHandleMethods(MyAdaptedController3.class);
|
||||
}
|
||||
|
||||
private void initServlet(final Class<?> controllerclass) throws ServletException {
|
||||
private void initServlet(final Class<?> controllerClass) throws ServletException {
|
||||
servlet = new DispatcherServlet() {
|
||||
@Override
|
||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent)
|
||||
throws BeansException {
|
||||
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
|
||||
GenericWebApplicationContext wac = new GenericWebApplicationContext();
|
||||
wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerclass));
|
||||
wac.registerBeanDefinition("controller", new RootBeanDefinition(controllerClass));
|
||||
RootBeanDefinition ppc = new RootBeanDefinition(PropertyPlaceholderConfigurer.class);
|
||||
ppc.getPropertyValues().addPropertyValue("properties", "myKey=foo");
|
||||
wac.registerBeanDefinition("ppc", ppc);
|
||||
wac.refresh();
|
||||
return wac;
|
||||
}
|
||||
|
|
@ -1615,6 +1635,18 @@ public class ServletAnnotationControllerTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
public static class DefaultExpressionValueParamController {
|
||||
|
||||
@RequestMapping("/myPath.do")
|
||||
public void myHandle(@RequestParam(value = "id", defaultValue = "${myKey}") String id,
|
||||
@RequestHeader(defaultValue = "#{systemProperties.myHeader}") String header,
|
||||
@Value("#{request.contextPath}") String contextPath, HttpServletResponse response)
|
||||
throws IOException {
|
||||
response.getWriter().write(String.valueOf(id) + "-" + String.valueOf(header) + "-" + contextPath);
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
public static class MethodNotAllowedController {
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
|
|
@ -68,11 +69,11 @@ import org.springframework.web.context.request.WebRequest;
|
|||
import org.springframework.web.multipart.MultipartRequest;
|
||||
|
||||
/**
|
||||
* Support class for invoking an annotated handler method. Operates on the introspection results of a {@link
|
||||
* HandlerMethodResolver} for a specific handler type.
|
||||
* Support class for invoking an annotated handler method. Operates on the introspection results of a
|
||||
* {@link HandlerMethodResolver} for a specific handler type.
|
||||
*
|
||||
* <p>Used by {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} and {@link
|
||||
* org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter}.
|
||||
* <p>Used by {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} and
|
||||
* {@link org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Arjen Poutsma
|
||||
|
|
@ -213,6 +214,9 @@ public class HandlerMethodInvoker {
|
|||
attrName = attr.value();
|
||||
found++;
|
||||
}
|
||||
else if (Value.class.isInstance(paramAnn)) {
|
||||
defaultValue = ((Value) paramAnn).value();
|
||||
}
|
||||
else if ("Valid".equals(paramAnn.annotationType().getSimpleName())) {
|
||||
validate = true;
|
||||
}
|
||||
|
|
@ -228,6 +232,9 @@ public class HandlerMethodInvoker {
|
|||
if (argValue != WebArgumentResolver.UNRESOLVED) {
|
||||
args[i] = argValue;
|
||||
}
|
||||
else if (defaultValue != null) {
|
||||
args[i] = resolveDefaultValue(defaultValue);
|
||||
}
|
||||
else {
|
||||
Class paramType = methodParam.getParameterType();
|
||||
if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
|
||||
|
|
@ -399,7 +406,7 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
if (paramValue == null) {
|
||||
if (StringUtils.hasText(defaultValue)) {
|
||||
paramValue = defaultValue;
|
||||
paramValue = resolveDefaultValue(defaultValue);
|
||||
}
|
||||
else if (required) {
|
||||
raiseMissingParameterException(paramName, paramType);
|
||||
|
|
@ -426,7 +433,7 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
if (headerValue == null) {
|
||||
if (StringUtils.hasText(defaultValue)) {
|
||||
headerValue = defaultValue;
|
||||
headerValue = resolveDefaultValue(defaultValue);
|
||||
}
|
||||
else if (required) {
|
||||
raiseMissingHeaderException(headerName, paramType);
|
||||
|
|
@ -493,7 +500,7 @@ public class HandlerMethodInvoker {
|
|||
Object cookieValue = resolveCookieValue(cookieName, paramType, webRequest);
|
||||
if (cookieValue == null) {
|
||||
if (StringUtils.hasText(defaultValue)) {
|
||||
cookieValue = defaultValue;
|
||||
cookieValue = resolveDefaultValue(defaultValue);
|
||||
}
|
||||
else if (required) {
|
||||
raiseMissingCookieException(cookieName, paramType);
|
||||
|
|
@ -670,6 +677,10 @@ public class HandlerMethodInvoker {
|
|||
}
|
||||
}
|
||||
|
||||
protected Object resolveDefaultValue(String value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
protected Object resolveCommonArgument(MethodParameter methodParameter, NativeWebRequest webRequest)
|
||||
throws Exception {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue