Browse Source

Polish ModelFactory

pull/547/merge
Rossen Stoyanchev 12 years ago
parent
commit
e374769ecc
  1. 115
      spring-web/src/main/java/org/springframework/web/method/annotation/ModelFactory.java

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -33,7 +33,6 @@ import org.springframework.validation.BindingResult;
import org.springframework.web.HttpSessionRequiredException; import org.springframework.web.HttpSessionRequiredException;
import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.HandlerMethod;
@ -42,57 +41,59 @@ import org.springframework.web.method.support.ModelAndViewContainer;
/** /**
* Provides methods to initialize the {@link Model} before controller method * Provides methods to initialize the {@link Model} before controller method
* invocation and to update it afterwards. On initialization, the model is * invocation and to update it afterwards.
* populated with attributes from the session or by invoking *
* {@code @ModelAttribute} methods. On update, model attributes are * <p>On initialization, the model is populated with attributes from the session
* synchronized with the session -- either adding or removing them. * and by invoking methods annotated with {@code @ModelAttribute}.
* Also {@link BindingResult} attributes where missing. *
* <p>On update, model attributes are synchronized with the session and also
* {@link BindingResult} attributes are added where missing.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 3.1 * @since 3.1
*/ */
public final class ModelFactory { public final class ModelFactory {
private final List<InvocableHandlerMethod> attributeMethods; private final List<InvocableHandlerMethod> handlerMethods;
private final WebDataBinderFactory binderFactory; private final WebDataBinderFactory dataBinderFactory;
private final SessionAttributesHandler sessionAttributesHandler; private final SessionAttributesHandler sessionAttributesHandler;
/** /**
* Create a new instance with the given {@code @ModelAttribute} methods. * Create a new instance with the given {@code @ModelAttribute} methods.
* @param attributeMethods for model initialization * @param handlerMethods the {@code @ModelAttribute} methods to invoke
* @param binderFactory for adding {@link BindingResult} attributes * @param dataBinderFactory for preparation of {@link BindingResult} attributes
* @param sessionAttributesHandler for access to session attributes * @param sessionAttributesHandler for access to session attributes
*/ */
public ModelFactory(List<InvocableHandlerMethod> attributeMethods, public ModelFactory(List<InvocableHandlerMethod> handlerMethods, WebDataBinderFactory dataBinderFactory,
WebDataBinderFactory binderFactory, SessionAttributesHandler sessionAttributesHandler) {
SessionAttributesHandler sessionAttributesHandler) {
this.attributeMethods = (attributeMethods != null) ? attributeMethods : new ArrayList<InvocableHandlerMethod>(); this.handlerMethods = (handlerMethods != null) ? handlerMethods : new ArrayList<InvocableHandlerMethod>();
this.binderFactory = binderFactory; this.dataBinderFactory = dataBinderFactory;
this.sessionAttributesHandler = sessionAttributesHandler; this.sessionAttributesHandler = sessionAttributesHandler;
} }
/** /**
* Populate the model in the following order: * Populate the model in the following order:
* <ol> * <ol>
* <li>Retrieve "known" session attributes -- i.e. attributes listed via * <li>Retrieve "known" session attributes listed as {@code @SessionAttributes}.
* {@link SessionAttributes @SessionAttributes} and previously stored in * <li>Invoke {@code @ModelAttribute} methods
* the in the model at least once * <li>Find {@code @ModelAttribute} method arguments also listed as
* <li>Invoke {@link ModelAttribute @ModelAttribute} methods * {@code @SessionAttributes} and ensure they're present in the model raising
* <li>Find method arguments eligible as session attributes and retrieve * an exception if necessary.
* them if they're not already present in the model
* </ol> * </ol>
* @param request the current request * @param request the current request
* @param mavContainer contains the model to be initialized * @param mavContainer a container with the model to be initialized
* @param handlerMethod the method for which the model is initialized * @param handlerMethod the method for which the model is initialized
* @throws Exception may arise from {@code @ModelAttribute} methods * @throws Exception may arise from {@code @ModelAttribute} methods
*/ */
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod) public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
throws Exception { throws Exception {
Map<String, ?> attributesInSession = this.sessionAttributesHandler.retrieveAttributes(request); Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
mavContainer.mergeAttributes(attributesInSession); mavContainer.mergeAttributes(sessionAttributes);
invokeModelAttributeMethods(request, mavContainer); invokeModelAttributeMethods(request, mavContainer);
@ -108,13 +109,13 @@ public final class ModelFactory {
} }
/** /**
* Invoke model attribute methods to populate the model. Attributes are * Invoke model attribute methods to populate the model.
* added only if not already present in 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 mavContainer)
throws Exception { throws Exception {
for (InvocableHandlerMethod attrMethod : this.attributeMethods) { for (InvocableHandlerMethod attrMethod : this.handlerMethods) {
String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value(); String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();
if (mavContainer.containsAttribute(modelName)) { if (mavContainer.containsAttribute(modelName)) {
continue; continue;
@ -132,15 +133,14 @@ public final class ModelFactory {
} }
/** /**
* Return all {@code @ModelAttribute} arguments declared as session * Find {@code @ModelAttribute} arguments also listed as {@code @SessionAttributes}.
* attributes via {@code @SessionAttributes}.
*/ */
private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) { private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) {
List<String> result = new ArrayList<String>(); List<String> result = new ArrayList<String>();
for (MethodParameter param : handlerMethod.getMethodParameters()) { for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
if (param.hasParameterAnnotation(ModelAttribute.class)) { if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
String name = getNameForParameter(param); String name = getNameForParameter(parameter);
if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, param.getParameterType())) { if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, parameter.getParameterType())) {
result.add(name); result.add(name);
} }
} }
@ -149,11 +149,24 @@ public final class ModelFactory {
} }
/** /**
* Derive the model attribute name for the given return value using * Derives the model attribute name for a method parameter based on:
* one of the following: * <ol>
* <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);
}
/**
* Derive the model attribute name for the given return value using one of:
* <ol> * <ol>
* <li>The method {@code ModelAttribute} annotation value * <li>The method {@code ModelAttribute} annotation value
* <li>The declared return type if it is other than {@code Object} * <li>The declared return type if it is more specific than {@code Object}
* <li>The actual return value type * <li>The actual return value type
* </ol> * </ol>
* @param returnValue the value returned from a method invocation * @param returnValue the value returned from a method invocation
@ -161,9 +174,9 @@ public final class ModelFactory {
* @return the model name, never {@code null} nor empty * @return the model name, never {@code null} nor empty
*/ */
public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) { public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) {
ModelAttribute annot = returnType.getMethodAnnotation(ModelAttribute.class); ModelAttribute annotation = returnType.getMethodAnnotation(ModelAttribute.class);
if (annot != null && StringUtils.hasText(annot.value())) { if (annotation != null && StringUtils.hasText(annotation.value())) {
return annot.value(); return annotation.value();
} }
else { else {
Method method = returnType.getMethod(); Method method = returnType.getMethod();
@ -173,35 +186,19 @@ public final class ModelFactory {
} }
/** /**
* Derives the model attribute name for a method parameter based on: * Promote model attributes listed as {@code @SessionAttributes} to the session.
* <ol> * Add {@link BindingResult} attributes where necessary.
* <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);
}
/**
* Synchronize model attributes with the session. Add {@link BindingResult}
* attributes where necessary.
* @param request the current request * @param request the current request
* @param mavContainer contains the model to update * @param mavContainer contains the model to update
* @throws Exception if creating BindingResult attributes fails * @throws Exception if creating BindingResult attributes fails
*/ */
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception { public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
if (mavContainer.getSessionStatus().isComplete()){ if (mavContainer.getSessionStatus().isComplete()){
this.sessionAttributesHandler.cleanupAttributes(request); this.sessionAttributesHandler.cleanupAttributes(request);
} }
else { else {
this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel()); this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());
} }
if (!mavContainer.isRequestHandled()) { if (!mavContainer.isRequestHandled()) {
updateBindingResult(request, mavContainer.getModel()); updateBindingResult(request, mavContainer.getModel());
} }
@ -219,7 +216,7 @@ public final class ModelFactory {
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name; String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
if (!model.containsAttribute(bindingResultKey)) { if (!model.containsAttribute(bindingResultKey)) {
WebDataBinder dataBinder = binderFactory.createBinder(request, value, name); WebDataBinder dataBinder = dataBinderFactory.createBinder(request, value, name);
model.put(bindingResultKey, dataBinder.getBindingResult()); model.put(bindingResultKey, dataBinder.getBindingResult());
} }
} }

Loading…
Cancel
Save