001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.mvc.method.annotation; 018 019import java.lang.reflect.Method; 020import java.util.List; 021 022import org.springframework.core.MethodParameter; 023import org.springframework.lang.Nullable; 024import org.springframework.ui.ExtendedModelMap; 025import org.springframework.util.Assert; 026import org.springframework.web.context.request.NativeWebRequest; 027import org.springframework.web.method.annotation.ModelAttributeMethodProcessor; 028import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 029import org.springframework.web.method.support.ModelAndViewContainer; 030import org.springframework.web.servlet.ModelAndView; 031import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver; 032 033/** 034 * This return value handler is intended to be ordered after all others as it 035 * attempts to handle _any_ return value type (i.e. returns {@code true} for 036 * all return types). 037 * 038 * <p>The return value is handled either with a {@link ModelAndViewResolver} 039 * or otherwise by regarding it as a model attribute if it is a non-simple 040 * type. If neither of these succeeds (essentially simple type other than 041 * String), {@link UnsupportedOperationException} is raised. 042 * 043 * <p><strong>Note:</strong> This class is primarily needed to support 044 * {@link ModelAndViewResolver}, which unfortunately cannot be properly 045 * adapted to the {@link HandlerMethodReturnValueHandler} contract since the 046 * {@link HandlerMethodReturnValueHandler#supportsReturnType} method 047 * cannot be implemented. Hence {@code ModelAndViewResolver}s are limited 048 * to always being invoked at the end after all other return value 049 * handlers have been given a chance. It is recommended to re-implement 050 * a {@code ModelAndViewResolver} as {@code HandlerMethodReturnValueHandler}, 051 * which also provides better access to the return type and method information. 052 * 053 * @author Rossen Stoyanchev 054 * @since 3.1 055 */ 056public class ModelAndViewResolverMethodReturnValueHandler implements HandlerMethodReturnValueHandler { 057 058 @Nullable 059 private final List<ModelAndViewResolver> mavResolvers; 060 061 private final ModelAttributeMethodProcessor modelAttributeProcessor = new ModelAttributeMethodProcessor(true); 062 063 064 /** 065 * Create a new instance. 066 */ 067 public ModelAndViewResolverMethodReturnValueHandler(@Nullable List<ModelAndViewResolver> mavResolvers) { 068 this.mavResolvers = mavResolvers; 069 } 070 071 072 /** 073 * Always returns {@code true}. See class-level note. 074 */ 075 @Override 076 public boolean supportsReturnType(MethodParameter returnType) { 077 return true; 078 } 079 080 @Override 081 public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, 082 ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 083 084 if (this.mavResolvers != null) { 085 for (ModelAndViewResolver mavResolver : this.mavResolvers) { 086 Class<?> handlerType = returnType.getContainingClass(); 087 Method method = returnType.getMethod(); 088 Assert.state(method != null, "No handler method"); 089 ExtendedModelMap model = (ExtendedModelMap) mavContainer.getModel(); 090 ModelAndView mav = mavResolver.resolveModelAndView(method, handlerType, returnValue, model, webRequest); 091 if (mav != ModelAndViewResolver.UNRESOLVED) { 092 mavContainer.addAllAttributes(mav.getModel()); 093 mavContainer.setViewName(mav.getViewName()); 094 if (!mav.isReference()) { 095 mavContainer.setView(mav.getView()); 096 } 097 return; 098 } 099 } 100 } 101 102 // No suitable ModelAndViewResolver... 103 if (this.modelAttributeProcessor.supportsReturnType(returnType)) { 104 this.modelAttributeProcessor.handleReturnValue(returnValue, returnType, mavContainer, webRequest); 105 } 106 else { 107 throw new UnsupportedOperationException("Unexpected return type: " + 108 returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); 109 } 110 } 111 112}