While learning the Spring framework, I notice in the book Spring in Action, the author doesn\'t use ModelandView method return type in controllers. The aut
Here's an in depth look.
Spring offers a DispatcherServlet class that, typically, handles all your requests. It does this in its doDispatch(HttpServletRequest request, HttpServletResponse response) method
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
where mv is the final ModelAndView object, ha is a wrapper to your controller method annotated with @RequestMapping.
This will usually go through a stack of method calls ending up at ServletInvocableHandlerMethod.invokeAndHandle
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle
at org.springframework.web.servlet.DispatcherServlet.doDispatch
Looking at the source
public final void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
returnValue is the object returned by your @RequestMapping method. It goes through
this.returnValueHandlers.handleReturnValue
where Spring determines a HandlerMethodReturnValueHandler to handle that object.
public void handleReturnValue(
Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws Exception {
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType); // returns the appropriate handler
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
getReturnValueHandler(returnType); returns the appropriate handler. The HandlerMethodReturnValueHandler is an interface with a supportsReturnType method that returns true if the handler supports that type (String, View, ResponseEntity, etc. (look for supported return types)). So the method returns the first handler it finds that supports that type and runs it.
Spring, at initialization, registers a whole slew of implementations of HandlerMethodReturnValueHandler. Basically all the known implementing classes in its javadoc.
For example, if you return a String, Spring will use a ViewNameMethodReturnValueHandler to handle the response.
Now, which return type to use is up to you. If you wanted to return a Model so you can use request attributes in your jsp view, you can either have Spring pass a Model instance to your method or you can create the Model object yourself and pass it to a ModelAndView which your return. It's a matter of style in most cases.
In spring source code, you can see this class org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter. In the method public ModelAndView getModelAndView(...), you can get how sping-mvc generate ModelAandView object.
if (returnValue instanceof HttpEntity) { // returnValue is returned Value of Handler method
handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
return null;
}
else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
handleResponseBody(returnValue, webRequest);
return null;
}
else if (returnValue instanceof ModelAndView) {
ModelAndView mav = (ModelAndView) returnValue;
mav.getModelMap().mergeAttributes(implicitModel);
return mav;
}
else if (returnValue instanceof Model) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
}
else if (returnValue instanceof View) {
return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
}
else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
else if (returnValue instanceof Map) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue);
}
else if (returnValue instanceof String) { // String is here, return new ModelAndView
return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
}
So in this method you can learn that spring-mvc can handle many returned types of handler method to build ModleAndView object.
Functionality wise there is no difference, both these are equivalent:
@RequestMapping(..)
public String requestMapping1(Model model){
model.addAttribute("attr1", attr1);
return "viewName";
}
@RequestMapping(..)
public ModelAndView requestMapping2(){
ModelAndView modelAndView = new ModelAndView("viewName");
modelAndView.addObject("attr1", attr1);
return modelAndView;
}
However the preferred approach is the former and that is the reason why the author has not used the latter in the book samples.