001/* 002 * Copyright 2002-2019 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.ArrayList; 021import java.util.LinkedHashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Map.Entry; 025import java.util.Set; 026import java.util.concurrent.Callable; 027import java.util.concurrent.ConcurrentHashMap; 028import javax.servlet.http.HttpServletRequest; 029import javax.servlet.http.HttpServletResponse; 030import javax.servlet.http.HttpSession; 031import javax.xml.transform.Source; 032 033import org.springframework.beans.factory.BeanFactory; 034import org.springframework.beans.factory.BeanFactoryAware; 035import org.springframework.beans.factory.InitializingBean; 036import org.springframework.beans.factory.config.ConfigurableBeanFactory; 037import org.springframework.core.DefaultParameterNameDiscoverer; 038import org.springframework.core.MethodIntrospector; 039import org.springframework.core.ParameterNameDiscoverer; 040import org.springframework.core.annotation.AnnotationAwareOrderComparator; 041import org.springframework.core.annotation.AnnotationUtils; 042import org.springframework.core.task.AsyncTaskExecutor; 043import org.springframework.core.task.SimpleAsyncTaskExecutor; 044import org.springframework.http.converter.ByteArrayHttpMessageConverter; 045import org.springframework.http.converter.HttpMessageConverter; 046import org.springframework.http.converter.StringHttpMessageConverter; 047import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; 048import org.springframework.http.converter.xml.SourceHttpMessageConverter; 049import org.springframework.ui.ModelMap; 050import org.springframework.util.Assert; 051import org.springframework.util.CollectionUtils; 052import org.springframework.util.ReflectionUtils.MethodFilter; 053import org.springframework.web.accept.ContentNegotiationManager; 054import org.springframework.web.bind.annotation.InitBinder; 055import org.springframework.web.bind.annotation.ModelAttribute; 056import org.springframework.web.bind.annotation.RequestMapping; 057import org.springframework.web.bind.support.DefaultDataBinderFactory; 058import org.springframework.web.bind.support.DefaultSessionAttributeStore; 059import org.springframework.web.bind.support.SessionAttributeStore; 060import org.springframework.web.bind.support.WebBindingInitializer; 061import org.springframework.web.bind.support.WebDataBinderFactory; 062import org.springframework.web.context.request.NativeWebRequest; 063import org.springframework.web.context.request.ServletWebRequest; 064import org.springframework.web.context.request.WebRequest; 065import org.springframework.web.context.request.async.AsyncWebRequest; 066import org.springframework.web.context.request.async.CallableProcessingInterceptor; 067import org.springframework.web.context.request.async.DeferredResultProcessingInterceptor; 068import org.springframework.web.context.request.async.WebAsyncManager; 069import org.springframework.web.context.request.async.WebAsyncTask; 070import org.springframework.web.context.request.async.WebAsyncUtils; 071import org.springframework.web.method.ControllerAdviceBean; 072import org.springframework.web.method.HandlerMethod; 073import org.springframework.web.method.annotation.ErrorsMethodArgumentResolver; 074import org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver; 075import org.springframework.web.method.annotation.InitBinderDataBinderFactory; 076import org.springframework.web.method.annotation.MapMethodProcessor; 077import org.springframework.web.method.annotation.ModelAttributeMethodProcessor; 078import org.springframework.web.method.annotation.ModelFactory; 079import org.springframework.web.method.annotation.ModelMethodProcessor; 080import org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver; 081import org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver; 082import org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver; 083import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver; 084import org.springframework.web.method.annotation.SessionAttributesHandler; 085import org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver; 086import org.springframework.web.method.support.HandlerMethodArgumentResolver; 087import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite; 088import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 089import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite; 090import org.springframework.web.method.support.InvocableHandlerMethod; 091import org.springframework.web.method.support.ModelAndViewContainer; 092import org.springframework.web.servlet.ModelAndView; 093import org.springframework.web.servlet.View; 094import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver; 095import org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter; 096import org.springframework.web.servlet.mvc.support.RedirectAttributes; 097import org.springframework.web.servlet.support.RequestContextUtils; 098import org.springframework.web.util.WebUtils; 099 100/** 101 * An {@link AbstractHandlerMethodAdapter} that supports {@link HandlerMethod}s 102 * with their method argument and return type signature, as defined via 103 * {@code @RequestMapping}. 104 * 105 * <p>Support for custom argument and return value types can be added via 106 * {@link #setCustomArgumentResolvers} and {@link #setCustomReturnValueHandlers}. 107 * Or alternatively, to re-configure all argument and return value types, 108 * use {@link #setArgumentResolvers} and {@link #setReturnValueHandlers}. 109 * 110 * @author Rossen Stoyanchev 111 * @author Juergen Hoeller 112 * @author Sam Brannen 113 * @since 3.1 114 * @see HandlerMethodArgumentResolver 115 * @see HandlerMethodReturnValueHandler 116 */ 117public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter 118 implements BeanFactoryAware, InitializingBean { 119 120 /** 121 * MethodFilter that matches {@link InitBinder @InitBinder} methods. 122 */ 123 public static final MethodFilter INIT_BINDER_METHODS = new MethodFilter() { 124 @Override 125 public boolean matches(Method method) { 126 return (AnnotationUtils.findAnnotation(method, InitBinder.class) != null); 127 } 128 }; 129 130 /** 131 * MethodFilter that matches {@link ModelAttribute @ModelAttribute} methods. 132 */ 133 public static final MethodFilter MODEL_ATTRIBUTE_METHODS = new MethodFilter() { 134 @Override 135 public boolean matches(Method method) { 136 return (AnnotationUtils.findAnnotation(method, RequestMapping.class) == null && 137 AnnotationUtils.findAnnotation(method, ModelAttribute.class) != null); 138 } 139 }; 140 141 142 private List<HandlerMethodArgumentResolver> customArgumentResolvers; 143 144 private HandlerMethodArgumentResolverComposite argumentResolvers; 145 146 private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers; 147 148 private List<HandlerMethodReturnValueHandler> customReturnValueHandlers; 149 150 private HandlerMethodReturnValueHandlerComposite returnValueHandlers; 151 152 private List<ModelAndViewResolver> modelAndViewResolvers; 153 154 private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager(); 155 156 private List<HttpMessageConverter<?>> messageConverters; 157 158 private List<Object> requestResponseBodyAdvice = new ArrayList<Object>(); 159 160 private WebBindingInitializer webBindingInitializer; 161 162 private AsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("MvcAsync"); 163 164 private Long asyncRequestTimeout; 165 166 private CallableProcessingInterceptor[] callableInterceptors = new CallableProcessingInterceptor[0]; 167 168 private DeferredResultProcessingInterceptor[] deferredResultInterceptors = new DeferredResultProcessingInterceptor[0]; 169 170 private boolean ignoreDefaultModelOnRedirect = false; 171 172 private int cacheSecondsForSessionAttributeHandlers = 0; 173 174 private boolean synchronizeOnSession = false; 175 176 private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore(); 177 178 private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); 179 180 private ConfigurableBeanFactory beanFactory; 181 182 183 private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = 184 new ConcurrentHashMap<Class<?>, SessionAttributesHandler>(64); 185 186 private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<Class<?>, Set<Method>>(64); 187 188 private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = 189 new LinkedHashMap<ControllerAdviceBean, Set<Method>>(); 190 191 private final Map<Class<?>, Set<Method>> modelAttributeCache = new ConcurrentHashMap<Class<?>, Set<Method>>(64); 192 193 private final Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = 194 new LinkedHashMap<ControllerAdviceBean, Set<Method>>(); 195 196 197 public RequestMappingHandlerAdapter() { 198 StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); 199 stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316 200 201 this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4); 202 this.messageConverters.add(new ByteArrayHttpMessageConverter()); 203 this.messageConverters.add(stringHttpMessageConverter); 204 this.messageConverters.add(new SourceHttpMessageConverter<Source>()); 205 this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); 206 } 207 208 209 /** 210 * Provide resolvers for custom argument types. Custom resolvers are ordered 211 * after built-in ones. To override the built-in support for argument 212 * resolution use {@link #setArgumentResolvers} instead. 213 */ 214 public void setCustomArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { 215 this.customArgumentResolvers = argumentResolvers; 216 } 217 218 /** 219 * Return the custom argument resolvers, or {@code null}. 220 */ 221 public List<HandlerMethodArgumentResolver> getCustomArgumentResolvers() { 222 return this.customArgumentResolvers; 223 } 224 225 /** 226 * Configure the complete list of supported argument types thus overriding 227 * the resolvers that would otherwise be configured by default. 228 */ 229 public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { 230 if (argumentResolvers == null) { 231 this.argumentResolvers = null; 232 } 233 else { 234 this.argumentResolvers = new HandlerMethodArgumentResolverComposite(); 235 this.argumentResolvers.addResolvers(argumentResolvers); 236 } 237 } 238 239 /** 240 * Return the configured argument resolvers, or possibly {@code null} if 241 * not initialized yet via {@link #afterPropertiesSet()}. 242 */ 243 public List<HandlerMethodArgumentResolver> getArgumentResolvers() { 244 return (this.argumentResolvers != null ? this.argumentResolvers.getResolvers() : null); 245 } 246 247 /** 248 * Configure the supported argument types in {@code @InitBinder} methods. 249 */ 250 public void setInitBinderArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { 251 if (argumentResolvers == null) { 252 this.initBinderArgumentResolvers = null; 253 } 254 else { 255 this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite(); 256 this.initBinderArgumentResolvers.addResolvers(argumentResolvers); 257 } 258 } 259 260 /** 261 * Return the argument resolvers for {@code @InitBinder} methods, or possibly 262 * {@code null} if not initialized yet via {@link #afterPropertiesSet()}. 263 */ 264 public List<HandlerMethodArgumentResolver> getInitBinderArgumentResolvers() { 265 return (this.initBinderArgumentResolvers != null ? this.initBinderArgumentResolvers.getResolvers() : null); 266 } 267 268 /** 269 * Provide handlers for custom return value types. Custom handlers are 270 * ordered after built-in ones. To override the built-in support for 271 * return value handling use {@link #setReturnValueHandlers}. 272 */ 273 public void setCustomReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { 274 this.customReturnValueHandlers = returnValueHandlers; 275 } 276 277 /** 278 * Return the custom return value handlers, or {@code null}. 279 */ 280 public List<HandlerMethodReturnValueHandler> getCustomReturnValueHandlers() { 281 return this.customReturnValueHandlers; 282 } 283 284 /** 285 * Configure the complete list of supported return value types thus 286 * overriding handlers that would otherwise be configured by default. 287 */ 288 public void setReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { 289 if (returnValueHandlers == null) { 290 this.returnValueHandlers = null; 291 } 292 else { 293 this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite(); 294 this.returnValueHandlers.addHandlers(returnValueHandlers); 295 } 296 } 297 298 /** 299 * Return the configured handlers, or possibly {@code null} if not 300 * initialized yet via {@link #afterPropertiesSet()}. 301 */ 302 public List<HandlerMethodReturnValueHandler> getReturnValueHandlers() { 303 return (this.returnValueHandlers != null ? this.returnValueHandlers.getHandlers() : null); 304 } 305 306 /** 307 * Provide custom {@link ModelAndViewResolver}s. 308 * <p><strong>Note:</strong> This method is available for backwards 309 * compatibility only. However, it is recommended to re-write a 310 * {@code ModelAndViewResolver} as {@link HandlerMethodReturnValueHandler}. 311 * An adapter between the two interfaces is not possible since the 312 * {@link HandlerMethodReturnValueHandler#supportsReturnType} method 313 * cannot be implemented. Hence {@code ModelAndViewResolver}s are limited 314 * to always being invoked at the end after all other return value 315 * handlers have been given a chance. 316 * <p>A {@code HandlerMethodReturnValueHandler} provides better access to 317 * the return type and controller method information and can be ordered 318 * freely relative to other return value handlers. 319 */ 320 public void setModelAndViewResolvers(List<ModelAndViewResolver> modelAndViewResolvers) { 321 this.modelAndViewResolvers = modelAndViewResolvers; 322 } 323 324 /** 325 * Return the configured {@link ModelAndViewResolver}s, or {@code null}. 326 */ 327 public List<ModelAndViewResolver> getModelAndViewResolvers() { 328 return this.modelAndViewResolvers; 329 } 330 331 /** 332 * Set the {@link ContentNegotiationManager} to use to determine requested media types. 333 * If not set, the default constructor is used. 334 */ 335 public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) { 336 this.contentNegotiationManager = contentNegotiationManager; 337 } 338 339 /** 340 * Provide the converters to use in argument resolvers and return value 341 * handlers that support reading and/or writing to the body of the 342 * request and response. 343 */ 344 public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) { 345 this.messageConverters = messageConverters; 346 } 347 348 /** 349 * Return the configured message body converters. 350 */ 351 public List<HttpMessageConverter<?>> getMessageConverters() { 352 return this.messageConverters; 353 } 354 355 /** 356 * Add one or more {@code RequestBodyAdvice} instances to intercept the 357 * request before it is read and converted for {@code @RequestBody} and 358 * {@code HttpEntity} method arguments. 359 */ 360 public void setRequestBodyAdvice(List<RequestBodyAdvice> requestBodyAdvice) { 361 if (requestBodyAdvice != null) { 362 this.requestResponseBodyAdvice.addAll(requestBodyAdvice); 363 } 364 } 365 366 /** 367 * Add one or more {@code ResponseBodyAdvice} instances to intercept the 368 * response before {@code @ResponseBody} or {@code ResponseEntity} return 369 * values are written to the response body. 370 */ 371 public void setResponseBodyAdvice(List<ResponseBodyAdvice<?>> responseBodyAdvice) { 372 if (responseBodyAdvice != null) { 373 this.requestResponseBodyAdvice.addAll(responseBodyAdvice); 374 } 375 } 376 377 /** 378 * Provide a WebBindingInitializer with "global" initialization to apply 379 * to every DataBinder instance. 380 */ 381 public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) { 382 this.webBindingInitializer = webBindingInitializer; 383 } 384 385 /** 386 * Return the configured WebBindingInitializer, or {@code null} if none. 387 */ 388 public WebBindingInitializer getWebBindingInitializer() { 389 return this.webBindingInitializer; 390 } 391 392 /** 393 * Set the default {@link AsyncTaskExecutor} to use when a controller method 394 * return a {@link Callable}. Controller methods can override this default on 395 * a per-request basis by returning an {@link WebAsyncTask}. 396 * <p>By default a {@link SimpleAsyncTaskExecutor} instance is used. 397 * It's recommended to change that default in production as the simple executor 398 * does not re-use threads. 399 */ 400 public void setTaskExecutor(AsyncTaskExecutor taskExecutor) { 401 this.taskExecutor = taskExecutor; 402 } 403 404 /** 405 * Specify the amount of time, in milliseconds, before concurrent handling 406 * should time out. In Servlet 3, the timeout begins after the main request 407 * processing thread has exited and ends when the request is dispatched again 408 * for further processing of the concurrently produced result. 409 * <p>If this value is not set, the default timeout of the underlying 410 * implementation is used, e.g. 10 seconds on Tomcat with Servlet 3. 411 * @param timeout the timeout value in milliseconds 412 */ 413 public void setAsyncRequestTimeout(long timeout) { 414 this.asyncRequestTimeout = timeout; 415 } 416 417 /** 418 * Configure {@code CallableProcessingInterceptor}'s to register on async requests. 419 * @param interceptors the interceptors to register 420 */ 421 public void setCallableInterceptors(List<CallableProcessingInterceptor> interceptors) { 422 Assert.notNull(interceptors, "CallableProcessingInterceptor List must not be null"); 423 this.callableInterceptors = interceptors.toArray(new CallableProcessingInterceptor[interceptors.size()]); 424 } 425 426 /** 427 * Configure {@code DeferredResultProcessingInterceptor}'s to register on async requests. 428 * @param interceptors the interceptors to register 429 */ 430 public void setDeferredResultInterceptors(List<DeferredResultProcessingInterceptor> interceptors) { 431 Assert.notNull(interceptors, "DeferredResultProcessingInterceptor List must not be null"); 432 this.deferredResultInterceptors = interceptors.toArray(new DeferredResultProcessingInterceptor[interceptors.size()]); 433 } 434 435 /** 436 * By default the content of the "default" model is used both during 437 * rendering and redirect scenarios. Alternatively a controller method 438 * can declare a {@link RedirectAttributes} argument and use it to provide 439 * attributes for a redirect. 440 * <p>Setting this flag to {@code true} guarantees the "default" model is 441 * never used in a redirect scenario even if a RedirectAttributes argument 442 * is not declared. Setting it to {@code false} means the "default" model 443 * may be used in a redirect if the controller method doesn't declare a 444 * RedirectAttributes argument. 445 * <p>The default setting is {@code false} but new applications should 446 * consider setting it to {@code true}. 447 * @see RedirectAttributes 448 */ 449 public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) { 450 this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect; 451 } 452 453 /** 454 * Specify the strategy to store session attributes with. The default is 455 * {@link org.springframework.web.bind.support.DefaultSessionAttributeStore}, 456 * storing session attributes in the HttpSession with the same attribute 457 * name as in the model. 458 */ 459 public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) { 460 this.sessionAttributeStore = sessionAttributeStore; 461 } 462 463 /** 464 * Cache content produced by {@code @SessionAttributes} annotated handlers 465 * for the given number of seconds. 466 * <p>Possible values are: 467 * <ul> 468 * <li>-1: no generation of cache-related headers</li> 469 * <li>0 (default value): "Cache-Control: no-store" will prevent caching</li> 470 * <li>1 or higher: "Cache-Control: max-age=seconds" will ask to cache content; 471 * not advised when dealing with session attributes</li> 472 * </ul> 473 * <p>In contrast to the "cacheSeconds" property which will apply to all general 474 * handlers (but not to {@code @SessionAttributes} annotated handlers), 475 * this setting will apply to {@code @SessionAttributes} handlers only. 476 * @see #setCacheSeconds 477 * @see org.springframework.web.bind.annotation.SessionAttributes 478 */ 479 public void setCacheSecondsForSessionAttributeHandlers(int cacheSecondsForSessionAttributeHandlers) { 480 this.cacheSecondsForSessionAttributeHandlers = cacheSecondsForSessionAttributeHandlers; 481 } 482 483 /** 484 * Set if controller execution should be synchronized on the session, 485 * to serialize parallel invocations from the same client. 486 * <p>More specifically, the execution of the {@code handleRequestInternal} 487 * method will get synchronized if this flag is "true". The best available 488 * session mutex will be used for the synchronization; ideally, this will 489 * be a mutex exposed by HttpSessionMutexListener. 490 * <p>The session mutex is guaranteed to be the same object during 491 * the entire lifetime of the session, available under the key defined 492 * by the {@code SESSION_MUTEX_ATTRIBUTE} constant. It serves as a 493 * safe reference to synchronize on for locking on the current session. 494 * <p>In many cases, the HttpSession reference itself is a safe mutex 495 * as well, since it will always be the same object reference for the 496 * same active logical session. However, this is not guaranteed across 497 * different servlet containers; the only 100% safe way is a session mutex. 498 * @see org.springframework.web.util.HttpSessionMutexListener 499 * @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession) 500 */ 501 public void setSynchronizeOnSession(boolean synchronizeOnSession) { 502 this.synchronizeOnSession = synchronizeOnSession; 503 } 504 505 /** 506 * Set the ParameterNameDiscoverer to use for resolving method parameter names if needed 507 * (e.g. for default attribute names). 508 * <p>Default is a {@link org.springframework.core.DefaultParameterNameDiscoverer}. 509 */ 510 public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { 511 this.parameterNameDiscoverer = parameterNameDiscoverer; 512 } 513 514 /** 515 * A {@link ConfigurableBeanFactory} is expected for resolving expressions 516 * in method argument default values. 517 */ 518 @Override 519 public void setBeanFactory(BeanFactory beanFactory) { 520 if (beanFactory instanceof ConfigurableBeanFactory) { 521 this.beanFactory = (ConfigurableBeanFactory) beanFactory; 522 } 523 } 524 525 /** 526 * Return the owning factory of this bean instance, or {@code null} if none. 527 */ 528 protected ConfigurableBeanFactory getBeanFactory() { 529 return this.beanFactory; 530 } 531 532 533 @Override 534 public void afterPropertiesSet() { 535 // Do this first, it may add ResponseBody advice beans 536 initControllerAdviceCache(); 537 538 if (this.argumentResolvers == null) { 539 List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); 540 this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); 541 } 542 if (this.initBinderArgumentResolvers == null) { 543 List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers(); 544 this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); 545 } 546 if (this.returnValueHandlers == null) { 547 List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers(); 548 this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); 549 } 550 } 551 552 private void initControllerAdviceCache() { 553 if (getApplicationContext() == null) { 554 return; 555 } 556 if (logger.isInfoEnabled()) { 557 logger.info("Looking for @ControllerAdvice: " + getApplicationContext()); 558 } 559 560 List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); 561 AnnotationAwareOrderComparator.sort(beans); 562 563 List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>(); 564 565 for (ControllerAdviceBean bean : beans) { 566 Class<?> beanType = bean.getBeanType(); 567 568 Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS); 569 if (!attrMethods.isEmpty()) { 570 this.modelAttributeAdviceCache.put(bean, attrMethods); 571 if (logger.isInfoEnabled()) { 572 logger.info("Detected @ModelAttribute methods in " + bean); 573 } 574 } 575 Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS); 576 if (!binderMethods.isEmpty()) { 577 this.initBinderAdviceCache.put(bean, binderMethods); 578 if (logger.isInfoEnabled()) { 579 logger.info("Detected @InitBinder methods in " + bean); 580 } 581 } 582 583 boolean isRequestBodyAdvice = RequestBodyAdvice.class.isAssignableFrom(beanType); 584 boolean isResponseBodyAdvice = ResponseBodyAdvice.class.isAssignableFrom(beanType); 585 if (isRequestBodyAdvice || isResponseBodyAdvice) { 586 requestResponseBodyAdviceBeans.add(bean); 587 if (logger.isInfoEnabled()) { 588 if (isRequestBodyAdvice) { 589 logger.info("Detected RequestBodyAdvice bean in " + bean); 590 } 591 else { 592 logger.info("Detected ResponseBodyAdvice bean in " + bean); 593 } 594 } 595 } 596 } 597 598 if (!requestResponseBodyAdviceBeans.isEmpty()) { 599 this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans); 600 } 601 } 602 603 /** 604 * Return the list of argument resolvers to use including built-in resolvers 605 * and custom resolvers provided via {@link #setCustomArgumentResolvers}. 606 */ 607 private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { 608 List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); 609 610 // Annotation-based argument resolution 611 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); 612 resolvers.add(new RequestParamMapMethodArgumentResolver()); 613 resolvers.add(new PathVariableMethodArgumentResolver()); 614 resolvers.add(new PathVariableMapMethodArgumentResolver()); 615 resolvers.add(new MatrixVariableMethodArgumentResolver()); 616 resolvers.add(new MatrixVariableMapMethodArgumentResolver()); 617 resolvers.add(new ServletModelAttributeMethodProcessor(false)); 618 resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); 619 resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); 620 resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); 621 resolvers.add(new RequestHeaderMapMethodArgumentResolver()); 622 resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); 623 resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); 624 resolvers.add(new SessionAttributeMethodArgumentResolver()); 625 resolvers.add(new RequestAttributeMethodArgumentResolver()); 626 627 // Type-based argument resolution 628 resolvers.add(new ServletRequestMethodArgumentResolver()); 629 resolvers.add(new ServletResponseMethodArgumentResolver()); 630 resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); 631 resolvers.add(new RedirectAttributesMethodArgumentResolver()); 632 resolvers.add(new ModelMethodProcessor()); 633 resolvers.add(new MapMethodProcessor()); 634 resolvers.add(new ErrorsMethodArgumentResolver()); 635 resolvers.add(new SessionStatusMethodArgumentResolver()); 636 resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); 637 638 // Custom arguments 639 if (getCustomArgumentResolvers() != null) { 640 resolvers.addAll(getCustomArgumentResolvers()); 641 } 642 643 // Catch-all 644 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); 645 resolvers.add(new ServletModelAttributeMethodProcessor(true)); 646 647 return resolvers; 648 } 649 650 /** 651 * Return the list of argument resolvers to use for {@code @InitBinder} 652 * methods including built-in and custom resolvers. 653 */ 654 private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() { 655 List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); 656 657 // Annotation-based argument resolution 658 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); 659 resolvers.add(new RequestParamMapMethodArgumentResolver()); 660 resolvers.add(new PathVariableMethodArgumentResolver()); 661 resolvers.add(new PathVariableMapMethodArgumentResolver()); 662 resolvers.add(new MatrixVariableMethodArgumentResolver()); 663 resolvers.add(new MatrixVariableMapMethodArgumentResolver()); 664 resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); 665 resolvers.add(new SessionAttributeMethodArgumentResolver()); 666 resolvers.add(new RequestAttributeMethodArgumentResolver()); 667 668 // Type-based argument resolution 669 resolvers.add(new ServletRequestMethodArgumentResolver()); 670 resolvers.add(new ServletResponseMethodArgumentResolver()); 671 672 // Custom arguments 673 if (getCustomArgumentResolvers() != null) { 674 resolvers.addAll(getCustomArgumentResolvers()); 675 } 676 677 // Catch-all 678 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); 679 680 return resolvers; 681 } 682 683 /** 684 * Return the list of return value handlers to use including built-in and 685 * custom handlers provided via {@link #setReturnValueHandlers}. 686 */ 687 private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() { 688 List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>(); 689 690 // Single-purpose return value types 691 handlers.add(new ModelAndViewMethodReturnValueHandler()); 692 handlers.add(new ModelMethodProcessor()); 693 handlers.add(new ViewMethodReturnValueHandler()); 694 handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters())); 695 handlers.add(new StreamingResponseBodyReturnValueHandler()); 696 handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), 697 this.contentNegotiationManager, this.requestResponseBodyAdvice)); 698 handlers.add(new HttpHeadersReturnValueHandler()); 699 handlers.add(new CallableMethodReturnValueHandler()); 700 handlers.add(new DeferredResultMethodReturnValueHandler()); 701 handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory)); 702 703 // Annotation-based return value types 704 handlers.add(new ModelAttributeMethodProcessor(false)); 705 handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), 706 this.contentNegotiationManager, this.requestResponseBodyAdvice)); 707 708 // Multi-purpose return value types 709 handlers.add(new ViewNameMethodReturnValueHandler()); 710 handlers.add(new MapMethodProcessor()); 711 712 // Custom return value types 713 if (getCustomReturnValueHandlers() != null) { 714 handlers.addAll(getCustomReturnValueHandlers()); 715 } 716 717 // Catch-all 718 if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) { 719 handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers())); 720 } 721 else { 722 handlers.add(new ModelAttributeMethodProcessor(true)); 723 } 724 725 return handlers; 726 } 727 728 729 /** 730 * Always return {@code true} since any method argument and return value 731 * type will be processed in some way. A method argument not recognized 732 * by any HandlerMethodArgumentResolver is interpreted as a request parameter 733 * if it is a simple type, or as a model attribute otherwise. A return value 734 * not recognized by any HandlerMethodReturnValueHandler will be interpreted 735 * as a model attribute. 736 */ 737 @Override 738 protected boolean supportsInternal(HandlerMethod handlerMethod) { 739 return true; 740 } 741 742 @Override 743 protected ModelAndView handleInternal(HttpServletRequest request, 744 HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { 745 746 ModelAndView mav; 747 checkRequest(request); 748 749 // Execute invokeHandlerMethod in synchronized block if required. 750 if (this.synchronizeOnSession) { 751 HttpSession session = request.getSession(false); 752 if (session != null) { 753 Object mutex = WebUtils.getSessionMutex(session); 754 synchronized (mutex) { 755 mav = invokeHandlerMethod(request, response, handlerMethod); 756 } 757 } 758 else { 759 // No HttpSession available -> no mutex necessary 760 mav = invokeHandlerMethod(request, response, handlerMethod); 761 } 762 } 763 else { 764 // No synchronization on session demanded at all... 765 mav = invokeHandlerMethod(request, response, handlerMethod); 766 } 767 768 if (!response.containsHeader(HEADER_CACHE_CONTROL)) { 769 if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { 770 applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); 771 } 772 else { 773 prepareResponse(response); 774 } 775 } 776 777 return mav; 778 } 779 780 /** 781 * This implementation always returns -1. An {@code @RequestMapping} method can 782 * calculate the lastModified value, call {@link WebRequest#checkNotModified(long)}, 783 * and return {@code null} if the result of that call is {@code true}. 784 */ 785 @Override 786 protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) { 787 return -1; 788 } 789 790 791 /** 792 * Return the {@link SessionAttributesHandler} instance for the given handler type 793 * (never {@code null}). 794 */ 795 private SessionAttributesHandler getSessionAttributesHandler(HandlerMethod handlerMethod) { 796 Class<?> handlerType = handlerMethod.getBeanType(); 797 SessionAttributesHandler sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType); 798 if (sessionAttrHandler == null) { 799 synchronized (this.sessionAttributesHandlerCache) { 800 sessionAttrHandler = this.sessionAttributesHandlerCache.get(handlerType); 801 if (sessionAttrHandler == null) { 802 sessionAttrHandler = new SessionAttributesHandler(handlerType, sessionAttributeStore); 803 this.sessionAttributesHandlerCache.put(handlerType, sessionAttrHandler); 804 } 805 } 806 } 807 return sessionAttrHandler; 808 } 809 810 /** 811 * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView} 812 * if view resolution is required. 813 * @since 4.2 814 * @see #createInvocableHandlerMethod(HandlerMethod) 815 */ 816 protected ModelAndView invokeHandlerMethod(HttpServletRequest request, 817 HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { 818 819 ServletWebRequest webRequest = new ServletWebRequest(request, response); 820 try { 821 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); 822 ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); 823 824 ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); 825 invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); 826 invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); 827 invocableMethod.setDataBinderFactory(binderFactory); 828 invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); 829 830 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); 831 mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); 832 modelFactory.initModel(webRequest, mavContainer, invocableMethod); 833 mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); 834 835 AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); 836 asyncWebRequest.setTimeout(this.asyncRequestTimeout); 837 838 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 839 asyncManager.setTaskExecutor(this.taskExecutor); 840 asyncManager.setAsyncWebRequest(asyncWebRequest); 841 asyncManager.registerCallableInterceptors(this.callableInterceptors); 842 asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); 843 844 if (asyncManager.hasConcurrentResult()) { 845 Object result = asyncManager.getConcurrentResult(); 846 mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; 847 asyncManager.clearConcurrentResult(); 848 if (logger.isDebugEnabled()) { 849 logger.debug("Found concurrent result value [" + result + "]"); 850 } 851 invocableMethod = invocableMethod.wrapConcurrentResult(result); 852 } 853 854 invocableMethod.invokeAndHandle(webRequest, mavContainer); 855 if (asyncManager.isConcurrentHandlingStarted()) { 856 return null; 857 } 858 859 return getModelAndView(mavContainer, modelFactory, webRequest); 860 } 861 finally { 862 webRequest.requestCompleted(); 863 } 864 } 865 866 /** 867 * Create a {@link ServletInvocableHandlerMethod} from the given {@link HandlerMethod} definition. 868 * @param handlerMethod the {@link HandlerMethod} definition 869 * @return the corresponding {@link ServletInvocableHandlerMethod} (or custom subclass thereof) 870 * @since 4.2 871 */ 872 protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) { 873 return new ServletInvocableHandlerMethod(handlerMethod); 874 } 875 876 private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) { 877 SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod); 878 Class<?> handlerType = handlerMethod.getBeanType(); 879 Set<Method> methods = this.modelAttributeCache.get(handlerType); 880 if (methods == null) { 881 methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS); 882 this.modelAttributeCache.put(handlerType, methods); 883 } 884 List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>(); 885 // Global methods first 886 for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) { 887 if (entry.getKey().isApplicableToBeanType(handlerType)) { 888 Object bean = entry.getKey().resolveBean(); 889 for (Method method : entry.getValue()) { 890 attrMethods.add(createModelAttributeMethod(binderFactory, bean, method)); 891 } 892 } 893 } 894 for (Method method : methods) { 895 Object bean = handlerMethod.getBean(); 896 attrMethods.add(createModelAttributeMethod(binderFactory, bean, method)); 897 } 898 return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler); 899 } 900 901 private InvocableHandlerMethod createModelAttributeMethod(WebDataBinderFactory factory, Object bean, Method method) { 902 InvocableHandlerMethod attrMethod = new InvocableHandlerMethod(bean, method); 903 attrMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); 904 attrMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); 905 attrMethod.setDataBinderFactory(factory); 906 return attrMethod; 907 } 908 909 private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception { 910 Class<?> handlerType = handlerMethod.getBeanType(); 911 Set<Method> methods = this.initBinderCache.get(handlerType); 912 if (methods == null) { 913 methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS); 914 this.initBinderCache.put(handlerType, methods); 915 } 916 List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>(); 917 // Global methods first 918 for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) { 919 if (entry.getKey().isApplicableToBeanType(handlerType)) { 920 Object bean = entry.getKey().resolveBean(); 921 for (Method method : entry.getValue()) { 922 initBinderMethods.add(createInitBinderMethod(bean, method)); 923 } 924 } 925 } 926 for (Method method : methods) { 927 Object bean = handlerMethod.getBean(); 928 initBinderMethods.add(createInitBinderMethod(bean, method)); 929 } 930 return createDataBinderFactory(initBinderMethods); 931 } 932 933 private InvocableHandlerMethod createInitBinderMethod(Object bean, Method method) { 934 InvocableHandlerMethod binderMethod = new InvocableHandlerMethod(bean, method); 935 binderMethod.setHandlerMethodArgumentResolvers(this.initBinderArgumentResolvers); 936 binderMethod.setDataBinderFactory(new DefaultDataBinderFactory(this.webBindingInitializer)); 937 binderMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); 938 return binderMethod; 939 } 940 941 /** 942 * Template method to create a new InitBinderDataBinderFactory instance. 943 * <p>The default implementation creates a ServletRequestDataBinderFactory. 944 * This can be overridden for custom ServletRequestDataBinder subclasses. 945 * @param binderMethods {@code @InitBinder} methods 946 * @return the InitBinderDataBinderFactory instance to use 947 * @throws Exception in case of invalid state or arguments 948 */ 949 protected InitBinderDataBinderFactory createDataBinderFactory(List<InvocableHandlerMethod> binderMethods) 950 throws Exception { 951 952 return new ServletRequestDataBinderFactory(binderMethods, getWebBindingInitializer()); 953 } 954 955 private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, 956 ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { 957 958 modelFactory.updateModel(webRequest, mavContainer); 959 if (mavContainer.isRequestHandled()) { 960 return null; 961 } 962 ModelMap model = mavContainer.getModel(); 963 ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); 964 if (!mavContainer.isViewReference()) { 965 mav.setView((View) mavContainer.getView()); 966 } 967 if (model instanceof RedirectAttributes) { 968 Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); 969 HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); 970 RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); 971 } 972 return mav; 973 } 974 975}