Skip to content

Commit 186fef6

Browse files
committed
Revised validation javadoc
Issue: SPR-12655
1 parent 3f8d48e commit 186fef6

File tree

5 files changed

+178
-172
lines changed

5 files changed

+178
-172
lines changed

spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121

2222
import org.apache.commons.logging.Log;
2323
import org.apache.commons.logging.LogFactory;
24+
2425
import org.springframework.beans.BeanUtils;
2526
import org.springframework.core.MethodParameter;
2627
import org.springframework.core.annotation.AnnotationUtils;
@@ -54,10 +55,11 @@
5455
*/
5556
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
5657

57-
protected Log logger = LogFactory.getLog(this.getClass());
58+
protected final Log logger = LogFactory.getLog(getClass());
5859

5960
private final boolean annotationNotRequired;
6061

62+
6163
/**
6264
* @param annotationNotRequired if "true", non-simple method arguments and
6365
* return values are considered model attributes with or without a
@@ -67,10 +69,12 @@ public ModelAttributeMethodProcessor(boolean annotationNotRequired) {
6769
this.annotationNotRequired = annotationNotRequired;
6870
}
6971

72+
7073
/**
71-
* @return true if the parameter is annotated with {@link ModelAttribute}
72-
* or in default resolution mode also if it is not a simple type.
74+
* Returns {@code true} if the parameter is annotated with {@link ModelAttribute}
75+
* or in default resolution mode, and also if it is not a simple type.
7376
*/
77+
@Override
7478
public boolean supportsParameter(MethodParameter parameter) {
7579
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
7680
return true;
@@ -92,18 +96,16 @@ else if (this.annotationNotRequired) {
9296
* and the next method parameter is not of type {@link Errors}.
9397
* @throws Exception if WebDataBinder initialization fails.
9498
*/
95-
public final Object resolveArgument(
96-
MethodParameter parameter, ModelAndViewContainer mavContainer,
97-
NativeWebRequest request, WebDataBinderFactory binderFactory)
98-
throws Exception {
99+
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
100+
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
99101

100102
String name = ModelFactory.getNameForParameter(parameter);
101-
Object attribute = (mavContainer.containsAttribute(name)) ?
102-
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
103+
Object attribute = (mavContainer.containsAttribute(name) ?
104+
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, webRequest));
103105

104-
WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
106+
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
105107
if (binder.getTarget() != null) {
106-
bindRequestParameters(binder, request);
108+
bindRequestParameters(binder, webRequest);
107109
validateIfApplicable(binder, parameter);
108110
if (binder.getBindingResult().hasErrors()) {
109111
if (isBindExceptionRequired(binder, parameter)) {
@@ -113,7 +115,6 @@ public final Object resolveArgument(
113115
}
114116

115117
// Add resolved attribute and BindingResult at the end of the model
116-
117118
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
118119
mavContainer.removeAttributes(bindingResultModel);
119120
mavContainer.addAllAttributes(bindingResultModel);
@@ -124,16 +125,16 @@ public final Object resolveArgument(
124125
/**
125126
* Extension point to create the model attribute if not found in the model.
126127
* The default implementation uses the default constructor.
127-
* @param attributeName the name of the attribute, never {@code null}
128-
* @param parameter the method parameter
128+
* @param attributeName the name of the attribute (never {@code null})
129+
* @param methodParam the method parameter
129130
* @param binderFactory for creating WebDataBinder instance
130131
* @param request the current request
131-
* @return the created model attribute, never {@code null}
132+
* @return the created model attribute (never {@code null})
132133
*/
133-
protected Object createAttribute(String attributeName, MethodParameter parameter,
134-
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
134+
protected Object createAttribute(String attributeName, MethodParameter methodParam,
135+
WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception {
135136

136-
return BeanUtils.instantiateClass(parameter.getParameterType());
137+
return BeanUtils.instantiateClass(methodParam.getParameterType());
137138
}
138139

139140
/**
@@ -147,15 +148,17 @@ protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest requ
147148

148149
/**
149150
* Validate the model attribute if applicable.
150-
* <p>The default implementation checks for {@code @javax.validation.Valid}.
151+
* <p>The default implementation checks for {@code @javax.validation.Valid},
152+
* Spring's {@link org.springframework.validation.annotation.Validated},
153+
* and custom annotations whose name starts with "Valid".
151154
* @param binder the DataBinder to be used
152-
* @param parameter the method parameter
155+
* @param methodParam the method parameter
153156
*/
154-
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
155-
Annotation[] annotations = parameter.getParameterAnnotations();
156-
for (Annotation annot : annotations) {
157-
if (annot.annotationType().getSimpleName().startsWith("Valid")) {
158-
Object hints = AnnotationUtils.getValue(annot);
157+
protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) {
158+
Annotation[] annotations = methodParam.getParameterAnnotations();
159+
for (Annotation ann : annotations) {
160+
if (ann.annotationType().getSimpleName().startsWith("Valid")) {
161+
Object hints = AnnotationUtils.getValue(ann);
159162
binder.validate(hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
160163
break;
161164
}
@@ -165,14 +168,13 @@ protected void validateIfApplicable(WebDataBinder binder, MethodParameter parame
165168
/**
166169
* Whether to raise a {@link BindException} on validation errors.
167170
* @param binder the data binder used to perform data binding
168-
* @param parameter the method argument
169-
* @return {@code true} if the next method argument is not of type {@link Errors}.
171+
* @param methodParam the method argument
172+
* @return {@code true} if the next method argument is not of type {@link Errors}
170173
*/
171-
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter) {
172-
int i = parameter.getParameterIndex();
173-
Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
174+
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter methodParam) {
175+
int i = methodParam.getParameterIndex();
176+
Class<?>[] paramTypes = methodParam.getMethod().getParameterTypes();
174177
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
175-
176178
return !hasBindingResult;
177179
}
178180

@@ -195,14 +197,13 @@ else if (this.annotationNotRequired) {
195197
/**
196198
* Add non-null return values to the {@link ModelAndViewContainer}.
197199
*/
198-
public void handleReturnValue(
199-
Object returnValue, MethodParameter returnType,
200-
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
201-
throws Exception {
200+
public void handleReturnValue(Object returnValue, MethodParameter returnType,
201+
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
202202

203203
if (returnValue != null) {
204204
String name = ModelFactory.getNameForReturnValue(returnValue, returnType);
205205
mavContainer.addAttribute(name, returnValue);
206206
}
207207
}
208+
208209
}

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,11 +25,11 @@
2525
import java.util.List;
2626
import java.util.Map;
2727
import java.util.Set;
28-
2928
import javax.servlet.http.HttpServletRequest;
3029

3130
import org.apache.commons.logging.Log;
3231
import org.apache.commons.logging.LogFactory;
32+
3333
import org.springframework.core.GenericTypeResolver;
3434
import org.springframework.core.MethodParameter;
3535
import org.springframework.http.HttpInputMessage;
@@ -59,12 +59,14 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
5959

6060
protected final List<MediaType> allSupportedMediaTypes;
6161

62+
6263
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> messageConverters) {
6364
Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
6465
this.messageConverters = messageConverters;
6566
this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters);
6667
}
6768

69+
6870
/**
6971
* Return the media types supported by all provided message converters sorted
7072
* by specificity via {@link MediaType#sortBySpecificity(List)}.
@@ -79,10 +81,10 @@ private static List<MediaType> getAllSupportedMediaTypes(List<HttpMessageConvert
7981
return Collections.unmodifiableList(result);
8082
}
8183

84+
8285
/**
83-
* Creates the method argument value of the expected parameter type by
86+
* Create the method argument value of the expected parameter type by
8487
* reading from the given request.
85-
*
8688
* @param <T> the expected type of the argument value to be created
8789
* @param webRequest the current request
8890
* @param methodParam the method argument
@@ -99,9 +101,8 @@ protected <T> Object readWithMessageConverters(NativeWebRequest webRequest,
99101
}
100102

101103
/**
102-
* Creates the method argument value of the expected parameter type by reading
104+
* Create the method argument value of the expected parameter type by reading
103105
* from the given HttpInputMessage.
104-
*
105106
* @param <T> the expected type of the argument value to be created
106107
* @param inputMessage the HTTP input message representing the current request
107108
* @param methodParam the method argument
@@ -123,43 +124,41 @@ protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,
123124
catch (InvalidMediaTypeException ex) {
124125
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
125126
}
126-
127127
if (contentType == null) {
128128
contentType = MediaType.APPLICATION_OCTET_STREAM;
129129
}
130130

131-
Class<?> contextClass = methodParam.getDeclaringClass();
132-
Map<TypeVariable, Type> map = GenericTypeResolver.getTypeVariableMap(contextClass);
133-
Class<T> targetClass = (Class<T>) GenericTypeResolver.resolveType(targetType, map);
134-
135-
for (HttpMessageConverter<?> converter : this.messageConverters) {
136-
if (converter instanceof GenericHttpMessageConverter) {
137-
GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;
138-
if (genericConverter.canRead(targetType, contextClass, contentType)) {
139-
if (logger.isDebugEnabled()) {
140-
logger.debug("Reading [" + targetType + "] as \"" +
141-
contentType + "\" using [" + converter + "]");
142-
}
143-
return genericConverter.read(targetType, contextClass, inputMessage);
144-
}
131+
Class<?> contextClass = methodParam.getDeclaringClass();
132+
Map<TypeVariable, Type> map = GenericTypeResolver.getTypeVariableMap(contextClass);
133+
Class<T> targetClass = (Class<T>) GenericTypeResolver.resolveType(targetType, map);
134+
135+
for (HttpMessageConverter<?> converter : this.messageConverters) {
136+
if (converter instanceof GenericHttpMessageConverter) {
137+
GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter;
138+
if (genericConverter.canRead(targetType, contextClass, contentType)) {
139+
if (logger.isDebugEnabled()) {
140+
logger.debug("Reading [" + targetType + "] as \"" +
141+
contentType + "\" using [" + converter + "]");
145142
}
146-
if (targetClass != null) {
147-
if (converter.canRead(targetClass, contentType)) {
148-
if (logger.isDebugEnabled()) {
149-
logger.debug("Reading [" + targetClass.getName() + "] as \"" +
150-
contentType + "\" using [" + converter + "]");
151-
}
152-
return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
153-
}
143+
return genericConverter.read(targetType, contextClass, inputMessage);
144+
}
145+
}
146+
if (targetClass != null) {
147+
if (converter.canRead(targetClass, contentType)) {
148+
if (logger.isDebugEnabled()) {
149+
logger.debug("Reading [" + targetClass.getName() + "] as \"" +
150+
contentType + "\" using [" + converter + "]");
154151
}
152+
return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
155153
}
156-
157-
throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
158154
}
155+
}
156+
157+
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
158+
}
159159

160160
/**
161-
* Creates a new {@link HttpInputMessage} from the given {@link NativeWebRequest}.
162-
*
161+
* Create a new {@link HttpInputMessage} from the given {@link NativeWebRequest}.
163162
* @param webRequest the web request to create an input message from
164163
* @return the input message
165164
*/

0 commit comments

Comments
 (0)