Polishing

(cherry picked from commit 9af12d2)
This commit is contained in:
Juergen Hoeller
2016-03-23 18:39:29 +01:00
parent 7c5aac122a
commit 2ea7fcde3e
23 changed files with 216 additions and 235 deletions

View File

@@ -130,7 +130,7 @@ public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConv
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return supports(clazz) && canWrite(mediaType);
}
}
/**
* Returns {@code true} if the given media type includes any of the

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 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.
@@ -45,14 +45,14 @@ import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Provides methods to initialize the {@link Model} before controller method
* invocation and to update it afterwards.
* Assist with initialization of the {@link Model} before controller method
* invocation and with updates to it after the invocation.
*
* <p>On initialization, the model is populated with attributes from the session
* and by invoking methods annotated with {@code @ModelAttribute}.
* <p>On initialization the model is populated with attributes temporarily stored
* in the session and through the invocation of {@code @ModelAttribute} methods.
*
* <p>On update, model attributes are synchronized with the session and also
* {@link BindingResult} attributes are added where missing.
* <p>On update model attributes are synchronized with the session and also
* {@link BindingResult} attributes are added if missing.
*
* @author Rossen Stoyanchev
* @since 3.1
@@ -70,51 +70,51 @@ public final class ModelFactory {
/**
* Create a new instance with the given {@code @ModelAttribute} methods.
* @param invocableMethods the {@code @ModelAttribute} methods to invoke
* @param dataBinderFactory for preparation of {@link BindingResult} attributes
* @param sessionAttributesHandler for access to session attributes
* @param handlerMethods the {@code @ModelAttribute} methods to invoke
* @param binderFactory for preparation of {@link BindingResult} attributes
* @param attributeHandler for access to session attributes
*/
public ModelFactory(List<InvocableHandlerMethod> invocableMethods, WebDataBinderFactory dataBinderFactory,
SessionAttributesHandler sessionAttributesHandler) {
public ModelFactory(List<InvocableHandlerMethod> handlerMethods,
WebDataBinderFactory binderFactory, SessionAttributesHandler attributeHandler) {
if (invocableMethods != null) {
for (InvocableHandlerMethod method : invocableMethods) {
this.modelMethods.add(new ModelMethod(method));
if (handlerMethods != null) {
for (InvocableHandlerMethod handlerMethod : handlerMethods) {
this.modelMethods.add(new ModelMethod(handlerMethod));
}
}
this.dataBinderFactory = dataBinderFactory;
this.sessionAttributesHandler = sessionAttributesHandler;
this.dataBinderFactory = binderFactory;
this.sessionAttributesHandler = attributeHandler;
}
/**
* Populate the model in the following order:
* <ol>
* <li>Retrieve "known" session attributes listed as {@code @SessionAttributes}.
* <li>Invoke {@code @ModelAttribute} methods
* <li>Find {@code @ModelAttribute} method arguments also listed as
* {@code @SessionAttributes} and ensure they're present in the model raising
* an exception if necessary.
* <li>Retrieve "known" session attributes listed as {@code @SessionAttributes}.
* <li>Invoke {@code @ModelAttribute} methods
* <li>Find {@code @ModelAttribute} method arguments also listed as
* {@code @SessionAttributes} and ensure they're present in the model raising
* an exception if necessary.
* </ol>
* @param request the current request
* @param mavContainer a container with the model to be initialized
* @param container a container with the model to be initialized
* @param handlerMethod the method for which the model is initialized
* @throws Exception may arise from {@code @ModelAttribute} methods
*/
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
throws Exception {
public void initModel(NativeWebRequest request, ModelAndViewContainer container,
HandlerMethod handlerMethod) throws Exception {
Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
mavContainer.mergeAttributes(sessionAttributes);
invokeModelAttributeMethods(request, mavContainer);
container.mergeAttributes(sessionAttributes);
invokeModelAttributeMethods(request, container);
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!mavContainer.containsAttribute(name)) {
if (!container.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");
}
mavContainer.addAttribute(name, value);
container.addAttribute(name, value);
}
}
}
@@ -123,30 +123,29 @@ public final class ModelFactory {
* Invoke model attribute methods to populate the model.
* Attributes are added only if not already present in the model.
*/
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container)
throws Exception {
while (!this.modelMethods.isEmpty()) {
InvocableHandlerMethod attrMethod = getNextModelMethod(mavContainer).getHandlerMethod();
String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();
if (mavContainer.containsAttribute(modelName)) {
InvocableHandlerMethod modelMethod = getNextModelMethod(container).getHandlerMethod();
String modelName = modelMethod.getMethodAnnotation(ModelAttribute.class).value();
if (container.containsAttribute(modelName)) {
continue;
}
Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
if (!attrMethod.isVoid()){
String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
if (!mavContainer.containsAttribute(returnValueName)) {
mavContainer.addAttribute(returnValueName, returnValue);
Object returnValue = modelMethod.invokeForRequest(request, container);
if (!modelMethod.isVoid()){
String returnValueName = getNameForReturnValue(returnValue, modelMethod.getReturnType());
if (!container.containsAttribute(returnValueName)) {
container.addAttribute(returnValueName, returnValue);
}
}
}
}
private ModelMethod getNextModelMethod(ModelAndViewContainer mavContainer) {
private ModelMethod getNextModelMethod(ModelAndViewContainer container) {
for (ModelMethod modelMethod : this.modelMethods) {
if (modelMethod.checkDependencies(mavContainer)) {
if (modelMethod.checkDependencies(container)) {
if (logger.isTraceEnabled()) {
logger.trace("Selected @ModelAttribute method " + modelMethod);
}
@@ -157,7 +156,7 @@ public final class ModelFactory {
ModelMethod modelMethod = this.modelMethods.get(0);
if (logger.isTraceEnabled()) {
logger.trace("Selected @ModelAttribute method (not present: " +
modelMethod.getUnresolvedDependencies(mavContainer)+ ") " + modelMethod);
modelMethod.getUnresolvedDependencies(container)+ ") " + modelMethod);
}
this.modelMethods.remove(modelMethod);
return modelMethod;
@@ -171,7 +170,8 @@ public final class ModelFactory {
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
String name = getNameForParameter(parameter);
if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, parameter.getParameterType())) {
Class<?> paramType = parameter.getParameterType();
if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, paramType)) {
result.add(name);
}
}
@@ -182,36 +182,37 @@ public final class ModelFactory {
/**
* Derives the model attribute name for a method parameter based on:
* <ol>
* <li>The parameter {@code @ModelAttribute} annotation value
* <li>The parameter type
* <li>The parameter {@code @ModelAttribute} annotation value
* <li>The parameter type
* </ol>
* @return the derived name; never {@code null} or an empty string
*/
public static String getNameForParameter(MethodParameter parameter) {
ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class);
String attrName = (annot != null) ? annot.value() : null;
return StringUtils.hasText(attrName) ? attrName : Conventions.getVariableNameForParameter(parameter);
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
String name = (ann != null ? ann.value() : null);
return StringUtils.hasText(name) ? name : Conventions.getVariableNameForParameter(parameter);
}
/**
* Derive the model attribute name for the given return value using one of:
* <ol>
* <li>The method {@code ModelAttribute} annotation value
* <li>The declared return type if it is more specific than {@code Object}
* <li>The actual return value type
* <li>The method {@code ModelAttribute} annotation value
* <li>The declared return type if it is more specific than {@code Object}
* <li>The actual return value type
* </ol>
* @param returnValue the value returned from a method invocation
* @param returnType the return type of the method
* @return the model name, never {@code null} nor empty
*/
public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) {
ModelAttribute annotation = returnType.getMethodAnnotation(ModelAttribute.class);
if (annotation != null && StringUtils.hasText(annotation.value())) {
return annotation.value();
ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class);
if (ann != null && StringUtils.hasText(ann.value())) {
return ann.value();
}
else {
Method method = returnType.getMethod();
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, returnType.getContainingClass());
Class<?> containingClass = returnType.getContainingClass();
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass);
return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
}
}
@@ -220,18 +221,18 @@ public final class ModelFactory {
* Promote model attributes listed as {@code @SessionAttributes} to the session.
* Add {@link BindingResult} attributes where necessary.
* @param request the current request
* @param mavContainer contains the model to update
* @param container contains the model to update
* @throws Exception if creating BindingResult attributes fails
*/
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
ModelMap defaultModel = mavContainer.getDefaultModel();
if (mavContainer.getSessionStatus().isComplete()){
public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
ModelMap defaultModel = container.getDefaultModel();
if (container.getSessionStatus().isComplete()){
this.sessionAttributesHandler.cleanupAttributes(request);
}
else {
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
}
if (!mavContainer.isRequestHandled() && mavContainer.getModel() == defaultModel) {
if (!container.isRequestHandled() && container.getModel() == defaultModel) {
updateBindingResult(request, defaultModel);
}
}
@@ -248,7 +249,7 @@ public final class ModelFactory {
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
if (!model.containsAttribute(bindingResultKey)) {
WebDataBinder dataBinder = dataBinderFactory.createBinder(request, value, name);
WebDataBinder dataBinder = this.dataBinderFactory.createBinder(request, value, name);
model.put(bindingResultKey, dataBinder.getBindingResult());
}
}
@@ -279,7 +280,6 @@ public final class ModelFactory {
private final Set<String> dependencies = new HashSet<String>();
private ModelMethod(InvocableHandlerMethod handlerMethod) {
this.handlerMethod = handlerMethod;
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 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.
@@ -24,7 +24,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
/**
@@ -76,7 +75,9 @@ public class HandlerMethodReturnValueHandlerComposite implements AsyncHandlerMet
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}