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