001/* 002 * Copyright 2002-2018 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.config.annotation; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Locale; 024import java.util.Map; 025import javax.servlet.ServletContext; 026import javax.servlet.http.HttpServletRequest; 027import javax.xml.transform.Source; 028 029import org.springframework.beans.BeanUtils; 030import org.springframework.beans.factory.BeanFactoryUtils; 031import org.springframework.beans.factory.BeanInitializationException; 032import org.springframework.context.ApplicationContext; 033import org.springframework.context.ApplicationContextAware; 034import org.springframework.context.annotation.Bean; 035import org.springframework.context.annotation.Configuration; 036import org.springframework.context.annotation.Lazy; 037import org.springframework.core.convert.converter.Converter; 038import org.springframework.format.Formatter; 039import org.springframework.format.FormatterRegistry; 040import org.springframework.format.support.DefaultFormattingConversionService; 041import org.springframework.format.support.FormattingConversionService; 042import org.springframework.http.MediaType; 043import org.springframework.http.converter.ByteArrayHttpMessageConverter; 044import org.springframework.http.converter.HttpMessageConverter; 045import org.springframework.http.converter.ResourceHttpMessageConverter; 046import org.springframework.http.converter.StringHttpMessageConverter; 047import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter; 048import org.springframework.http.converter.feed.RssChannelHttpMessageConverter; 049import org.springframework.http.converter.json.GsonHttpMessageConverter; 050import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; 051import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 052import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; 053import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter; 054import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; 055import org.springframework.http.converter.xml.SourceHttpMessageConverter; 056import org.springframework.util.AntPathMatcher; 057import org.springframework.util.Assert; 058import org.springframework.util.ClassUtils; 059import org.springframework.util.PathMatcher; 060import org.springframework.validation.Errors; 061import org.springframework.validation.MessageCodesResolver; 062import org.springframework.validation.Validator; 063import org.springframework.web.HttpRequestHandler; 064import org.springframework.web.accept.ContentNegotiationManager; 065import org.springframework.web.bind.WebDataBinder; 066import org.springframework.web.bind.support.ConfigurableWebBindingInitializer; 067import org.springframework.web.context.ServletContextAware; 068import org.springframework.web.cors.CorsConfiguration; 069import org.springframework.web.method.support.CompositeUriComponentsContributor; 070import org.springframework.web.method.support.HandlerMethodArgumentResolver; 071import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 072import org.springframework.web.servlet.HandlerAdapter; 073import org.springframework.web.servlet.HandlerExceptionResolver; 074import org.springframework.web.servlet.HandlerMapping; 075import org.springframework.web.servlet.ViewResolver; 076import org.springframework.web.servlet.handler.AbstractHandlerMapping; 077import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping; 078import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor; 079import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite; 080import org.springframework.web.servlet.handler.HandlerMappingIntrospector; 081import org.springframework.web.servlet.mvc.Controller; 082import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter; 083import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter; 084import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver; 085import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver; 086import org.springframework.web.servlet.mvc.method.annotation.JsonViewRequestBodyAdvice; 087import org.springframework.web.servlet.mvc.method.annotation.JsonViewResponseBodyAdvice; 088import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice; 089import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; 090import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 091import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; 092import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; 093import org.springframework.web.servlet.resource.ResourceUrlProvider; 094import org.springframework.web.servlet.resource.ResourceUrlProviderExposingInterceptor; 095import org.springframework.web.servlet.view.InternalResourceViewResolver; 096import org.springframework.web.servlet.view.ViewResolverComposite; 097import org.springframework.web.util.UrlPathHelper; 098 099/** 100 * This is the main class providing the configuration behind the MVC Java config. 101 * It is typically imported by adding {@link EnableWebMvc @EnableWebMvc} to an 102 * application {@link Configuration @Configuration} class. An alternative more 103 * advanced option is to extend directly from this class and override methods as 104 * necessary, remembering to add {@link Configuration @Configuration} to the 105 * subclass and {@link Bean @Bean} to overridden {@link Bean @Bean} methods. 106 * For more details see the javadoc of {@link EnableWebMvc @EnableWebMvc}. 107 * 108 * <p>This class registers the following {@link HandlerMapping HandlerMappings}:</p> 109 * <ul> 110 * <li>{@link RequestMappingHandlerMapping} 111 * ordered at 0 for mapping requests to annotated controller methods. 112 * <li>{@link HandlerMapping} 113 * ordered at 1 to map URL paths directly to view names. 114 * <li>{@link BeanNameUrlHandlerMapping} 115 * ordered at 2 to map URL paths to controller bean names. 116 * <li>{@link HandlerMapping} 117 * ordered at {@code Integer.MAX_VALUE-1} to serve static resource requests. 118 * <li>{@link HandlerMapping} 119 * ordered at {@code Integer.MAX_VALUE} to forward requests to the default servlet. 120 * </ul> 121 * 122 * <p>Registers these {@link HandlerAdapter HandlerAdapters}: 123 * <ul> 124 * <li>{@link RequestMappingHandlerAdapter} 125 * for processing requests with annotated controller methods. 126 * <li>{@link HttpRequestHandlerAdapter} 127 * for processing requests with {@link HttpRequestHandler HttpRequestHandlers}. 128 * <li>{@link SimpleControllerHandlerAdapter} 129 * for processing requests with interface-based {@link Controller Controllers}. 130 * </ul> 131 * 132 * <p>Registers a {@link HandlerExceptionResolverComposite} with this chain of 133 * exception resolvers: 134 * <ul> 135 * <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through 136 * {@link org.springframework.web.bind.annotation.ExceptionHandler} methods. 137 * <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with 138 * {@link org.springframework.web.bind.annotation.ResponseStatus}. 139 * <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring 140 * exception types 141 * </ul> 142 * 143 * <p>Registers an {@link AntPathMatcher} and a {@link UrlPathHelper} 144 * to be used by: 145 * <ul> 146 * <li>the {@link RequestMappingHandlerMapping}, 147 * <li>the {@link HandlerMapping} for ViewControllers 148 * <li>and the {@link HandlerMapping} for serving resources 149 * </ul> 150 * Note that those beans can be configured with a {@link PathMatchConfigurer}. 151 * 152 * <p>Both the {@link RequestMappingHandlerAdapter} and the 153 * {@link ExceptionHandlerExceptionResolver} are configured with default 154 * instances of the following by default: 155 * <ul> 156 * <li>a {@link ContentNegotiationManager} 157 * <li>a {@link DefaultFormattingConversionService} 158 * <li>an {@link org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean} 159 * if a JSR-303 implementation is available on the classpath 160 * <li>a range of {@link HttpMessageConverter HttpMessageConverters} depending on the third-party 161 * libraries available on the classpath. 162 * </ul> 163 * 164 * @author Rossen Stoyanchev 165 * @author Brian Clozel 166 * @author Sebastien Deleuze 167 * @since 3.1 168 * @see EnableWebMvc 169 * @see WebMvcConfigurer 170 * @see WebMvcConfigurerAdapter 171 */ 172public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware { 173 174 private static final boolean romePresent = 175 ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", 176 WebMvcConfigurationSupport.class.getClassLoader()); 177 178 private static final boolean jaxb2Present = 179 ClassUtils.isPresent("javax.xml.bind.Binder", 180 WebMvcConfigurationSupport.class.getClassLoader()); 181 182 private static final boolean jackson2Present = 183 ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", 184 WebMvcConfigurationSupport.class.getClassLoader()) && 185 ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", 186 WebMvcConfigurationSupport.class.getClassLoader()); 187 188 private static final boolean jackson2XmlPresent = 189 ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", 190 WebMvcConfigurationSupport.class.getClassLoader()); 191 192 private static final boolean gsonPresent = 193 ClassUtils.isPresent("com.google.gson.Gson", 194 WebMvcConfigurationSupport.class.getClassLoader()); 195 196 197 private ApplicationContext applicationContext; 198 199 private ServletContext servletContext; 200 201 private List<Object> interceptors; 202 203 private PathMatchConfigurer pathMatchConfigurer; 204 205 private ContentNegotiationManager contentNegotiationManager; 206 207 private List<HandlerMethodArgumentResolver> argumentResolvers; 208 209 private List<HandlerMethodReturnValueHandler> returnValueHandlers; 210 211 private List<HttpMessageConverter<?>> messageConverters; 212 213 private Map<String, CorsConfiguration> corsConfigurations; 214 215 216 /** 217 * Set the Spring {@link ApplicationContext}, e.g. for resource loading. 218 */ 219 @Override 220 public void setApplicationContext(ApplicationContext applicationContext) { 221 this.applicationContext = applicationContext; 222 } 223 224 /** 225 * Return the associated Spring {@link ApplicationContext}. 226 * @since 4.2 227 */ 228 public ApplicationContext getApplicationContext() { 229 return this.applicationContext; 230 } 231 232 /** 233 * Set the {@link javax.servlet.ServletContext}, e.g. for resource handling, 234 * looking up file extensions, etc. 235 */ 236 @Override 237 public void setServletContext(ServletContext servletContext) { 238 this.servletContext = servletContext; 239 } 240 241 /** 242 * Return the associated {@link javax.servlet.ServletContext}. 243 * @since 4.2 244 */ 245 public ServletContext getServletContext() { 246 return this.servletContext; 247 } 248 249 250 /** 251 * Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping 252 * requests to annotated controllers. 253 */ 254 @Bean 255 public RequestMappingHandlerMapping requestMappingHandlerMapping() { 256 RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping(); 257 mapping.setOrder(0); 258 mapping.setInterceptors(getInterceptors()); 259 mapping.setContentNegotiationManager(mvcContentNegotiationManager()); 260 mapping.setCorsConfigurations(getCorsConfigurations()); 261 262 PathMatchConfigurer configurer = getPathMatchConfigurer(); 263 264 Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch(); 265 if (useSuffixPatternMatch != null) { 266 mapping.setUseSuffixPatternMatch(useSuffixPatternMatch); 267 } 268 Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch(); 269 if (useRegisteredSuffixPatternMatch != null) { 270 mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch); 271 } 272 Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch(); 273 if (useTrailingSlashMatch != null) { 274 mapping.setUseTrailingSlashMatch(useTrailingSlashMatch); 275 } 276 277 UrlPathHelper pathHelper = configurer.getUrlPathHelper(); 278 if (pathHelper != null) { 279 mapping.setUrlPathHelper(pathHelper); 280 } 281 PathMatcher pathMatcher = configurer.getPathMatcher(); 282 if (pathMatcher != null) { 283 mapping.setPathMatcher(pathMatcher); 284 } 285 286 return mapping; 287 } 288 289 /** 290 * Protected method for plugging in a custom subclass of 291 * {@link RequestMappingHandlerMapping}. 292 * @since 4.0 293 */ 294 protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() { 295 return new RequestMappingHandlerMapping(); 296 } 297 298 /** 299 * Provide access to the shared handler interceptors used to configure 300 * {@link HandlerMapping} instances with. 301 * <p>This method cannot be overridden; use {@link #addInterceptors} instead. 302 */ 303 protected final Object[] getInterceptors() { 304 if (this.interceptors == null) { 305 InterceptorRegistry registry = new InterceptorRegistry(); 306 addInterceptors(registry); 307 registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService())); 308 registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); 309 this.interceptors = registry.getInterceptors(); 310 } 311 return this.interceptors.toArray(); 312 } 313 314 /** 315 * Override this method to add Spring MVC interceptors for 316 * pre- and post-processing of controller invocation. 317 * @see InterceptorRegistry 318 */ 319 protected void addInterceptors(InterceptorRegistry registry) { 320 } 321 322 /** 323 * Callback for building the {@link PathMatchConfigurer}. 324 * Delegates to {@link #configurePathMatch}. 325 * @since 4.1 326 */ 327 protected PathMatchConfigurer getPathMatchConfigurer() { 328 if (this.pathMatchConfigurer == null) { 329 this.pathMatchConfigurer = new PathMatchConfigurer(); 330 configurePathMatch(this.pathMatchConfigurer); 331 } 332 return this.pathMatchConfigurer; 333 } 334 335 /** 336 * Override this method to configure path matching options. 337 * @since 4.0.3 338 * @see PathMatchConfigurer 339 */ 340 protected void configurePathMatch(PathMatchConfigurer configurer) { 341 } 342 343 /** 344 * Return a global {@link PathMatcher} instance for path matching 345 * patterns in {@link HandlerMapping HandlerMappings}. 346 * This instance can be configured using the {@link PathMatchConfigurer} 347 * in {@link #configurePathMatch(PathMatchConfigurer)}. 348 * @since 4.1 349 */ 350 @Bean 351 public PathMatcher mvcPathMatcher() { 352 PathMatcher pathMatcher = getPathMatchConfigurer().getPathMatcher(); 353 return (pathMatcher != null ? pathMatcher : new AntPathMatcher()); 354 } 355 356 /** 357 * Return a global {@link UrlPathHelper} instance for path matching 358 * patterns in {@link HandlerMapping HandlerMappings}. 359 * This instance can be configured using the {@link PathMatchConfigurer} 360 * in {@link #configurePathMatch(PathMatchConfigurer)}. 361 * @since 4.1 362 */ 363 @Bean 364 public UrlPathHelper mvcUrlPathHelper() { 365 UrlPathHelper pathHelper = getPathMatchConfigurer().getUrlPathHelper(); 366 return (pathHelper != null ? pathHelper : new UrlPathHelper()); 367 } 368 369 /** 370 * Return a {@link ContentNegotiationManager} instance to use to determine 371 * requested {@linkplain MediaType media types} in a given request. 372 */ 373 @Bean 374 public ContentNegotiationManager mvcContentNegotiationManager() { 375 if (this.contentNegotiationManager == null) { 376 ContentNegotiationConfigurer configurer = new ContentNegotiationConfigurer(this.servletContext); 377 configurer.mediaTypes(getDefaultMediaTypes()); 378 configureContentNegotiation(configurer); 379 this.contentNegotiationManager = configurer.buildContentNegotiationManager(); 380 } 381 return this.contentNegotiationManager; 382 } 383 384 protected Map<String, MediaType> getDefaultMediaTypes() { 385 Map<String, MediaType> map = new HashMap<String, MediaType>(4); 386 if (romePresent) { 387 map.put("atom", MediaType.APPLICATION_ATOM_XML); 388 map.put("rss", MediaType.APPLICATION_RSS_XML); 389 } 390 if (jaxb2Present || jackson2XmlPresent) { 391 map.put("xml", MediaType.APPLICATION_XML); 392 } 393 if (jackson2Present || gsonPresent) { 394 map.put("json", MediaType.APPLICATION_JSON); 395 } 396 return map; 397 } 398 399 /** 400 * Override this method to configure content negotiation. 401 * @see DefaultServletHandlerConfigurer 402 */ 403 protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) { 404 } 405 406 /** 407 * Return a handler mapping ordered at 1 to map URL paths directly to 408 * view names. To configure view controllers, override 409 * {@link #addViewControllers}. 410 */ 411 @Bean 412 public HandlerMapping viewControllerHandlerMapping() { 413 ViewControllerRegistry registry = new ViewControllerRegistry(this.applicationContext); 414 addViewControllers(registry); 415 416 AbstractHandlerMapping handlerMapping = registry.buildHandlerMapping(); 417 handlerMapping = (handlerMapping != null ? handlerMapping : new EmptyHandlerMapping()); 418 handlerMapping.setPathMatcher(mvcPathMatcher()); 419 handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); 420 handlerMapping.setInterceptors(getInterceptors()); 421 handlerMapping.setCorsConfigurations(getCorsConfigurations()); 422 return handlerMapping; 423 } 424 425 /** 426 * Override this method to add view controllers. 427 * @see ViewControllerRegistry 428 */ 429 protected void addViewControllers(ViewControllerRegistry registry) { 430 } 431 432 /** 433 * Return a {@link BeanNameUrlHandlerMapping} ordered at 2 to map URL 434 * paths to controller bean names. 435 */ 436 @Bean 437 public BeanNameUrlHandlerMapping beanNameHandlerMapping() { 438 BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping(); 439 mapping.setOrder(2); 440 mapping.setInterceptors(getInterceptors()); 441 mapping.setCorsConfigurations(getCorsConfigurations()); 442 return mapping; 443 } 444 445 /** 446 * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped 447 * resource handlers. To configure resource handling, override 448 * {@link #addResourceHandlers}. 449 */ 450 @Bean 451 public HandlerMapping resourceHandlerMapping() { 452 Assert.state(this.applicationContext != null, "No ApplicationContext set"); 453 Assert.state(this.servletContext != null, "No ServletContext set"); 454 455 ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext, 456 this.servletContext, mvcContentNegotiationManager(), mvcUrlPathHelper()); 457 addResourceHandlers(registry); 458 459 AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); 460 if (handlerMapping != null) { 461 handlerMapping.setPathMatcher(mvcPathMatcher()); 462 handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); 463 handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); 464 handlerMapping.setCorsConfigurations(getCorsConfigurations()); 465 } 466 else { 467 handlerMapping = new EmptyHandlerMapping(); 468 } 469 return handlerMapping; 470 } 471 472 /** 473 * Override this method to add resource handlers for serving static resources. 474 * @see ResourceHandlerRegistry 475 */ 476 protected void addResourceHandlers(ResourceHandlerRegistry registry) { 477 } 478 479 /** 480 * A {@link ResourceUrlProvider} bean for use with the MVC dispatcher. 481 * @since 4.1 482 */ 483 @Bean 484 public ResourceUrlProvider mvcResourceUrlProvider() { 485 ResourceUrlProvider urlProvider = new ResourceUrlProvider(); 486 UrlPathHelper pathHelper = getPathMatchConfigurer().getUrlPathHelper(); 487 if (pathHelper != null) { 488 urlProvider.setUrlPathHelper(pathHelper); 489 } 490 PathMatcher pathMatcher = getPathMatchConfigurer().getPathMatcher(); 491 if (pathMatcher != null) { 492 urlProvider.setPathMatcher(pathMatcher); 493 } 494 return urlProvider; 495 } 496 497 /** 498 * Return a handler mapping ordered at Integer.MAX_VALUE with a mapped 499 * default servlet handler. To configure "default" Servlet handling, 500 * override {@link #configureDefaultServletHandling}. 501 */ 502 @Bean 503 public HandlerMapping defaultServletHandlerMapping() { 504 DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(this.servletContext); 505 configureDefaultServletHandling(configurer); 506 507 HandlerMapping handlerMapping = configurer.buildHandlerMapping(); 508 return (handlerMapping != null ? handlerMapping : new EmptyHandlerMapping()); 509 } 510 511 /** 512 * Override this method to configure "default" Servlet handling. 513 * @see DefaultServletHandlerConfigurer 514 */ 515 protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 516 } 517 518 /** 519 * Returns a {@link RequestMappingHandlerAdapter} for processing requests 520 * through annotated controller methods. Consider overriding one of these 521 * other more fine-grained methods: 522 * <ul> 523 * <li>{@link #addArgumentResolvers} for adding custom argument resolvers. 524 * <li>{@link #addReturnValueHandlers} for adding custom return value handlers. 525 * <li>{@link #configureMessageConverters} for adding custom message converters. 526 * </ul> 527 */ 528 @Bean 529 public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { 530 RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter(); 531 adapter.setContentNegotiationManager(mvcContentNegotiationManager()); 532 adapter.setMessageConverters(getMessageConverters()); 533 adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer()); 534 adapter.setCustomArgumentResolvers(getArgumentResolvers()); 535 adapter.setCustomReturnValueHandlers(getReturnValueHandlers()); 536 537 if (jackson2Present) { 538 adapter.setRequestBodyAdvice( 539 Collections.<RequestBodyAdvice>singletonList(new JsonViewRequestBodyAdvice())); 540 adapter.setResponseBodyAdvice( 541 Collections.<ResponseBodyAdvice<?>>singletonList(new JsonViewResponseBodyAdvice())); 542 } 543 544 AsyncSupportConfigurer configurer = new AsyncSupportConfigurer(); 545 configureAsyncSupport(configurer); 546 if (configurer.getTaskExecutor() != null) { 547 adapter.setTaskExecutor(configurer.getTaskExecutor()); 548 } 549 if (configurer.getTimeout() != null) { 550 adapter.setAsyncRequestTimeout(configurer.getTimeout()); 551 } 552 adapter.setCallableInterceptors(configurer.getCallableInterceptors()); 553 adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors()); 554 555 return adapter; 556 } 557 558 /** 559 * Protected method for plugging in a custom subclass of 560 * {@link RequestMappingHandlerAdapter}. 561 * @since 4.3 562 */ 563 protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() { 564 return new RequestMappingHandlerAdapter(); 565 } 566 567 /** 568 * Return the {@link ConfigurableWebBindingInitializer} to use for 569 * initializing all {@link WebDataBinder} instances. 570 */ 571 protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() { 572 ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer(); 573 initializer.setConversionService(mvcConversionService()); 574 initializer.setValidator(mvcValidator()); 575 initializer.setMessageCodesResolver(getMessageCodesResolver()); 576 return initializer; 577 } 578 579 /** 580 * Override this method to provide a custom {@link MessageCodesResolver}. 581 */ 582 protected MessageCodesResolver getMessageCodesResolver() { 583 return null; 584 } 585 586 /** 587 * Override this method to configure asynchronous request processing options. 588 * @see AsyncSupportConfigurer 589 */ 590 protected void configureAsyncSupport(AsyncSupportConfigurer configurer) { 591 } 592 593 /** 594 * Return a {@link FormattingConversionService} for use with annotated controllers. 595 * <p>See {@link #addFormatters} as an alternative to overriding this method. 596 */ 597 @Bean 598 public FormattingConversionService mvcConversionService() { 599 FormattingConversionService conversionService = new DefaultFormattingConversionService(); 600 addFormatters(conversionService); 601 return conversionService; 602 } 603 604 /** 605 * Override this method to add custom {@link Converter} and/or {@link Formatter} 606 * delegates to the common {@link FormattingConversionService}. 607 * @see #mvcConversionService() 608 */ 609 protected void addFormatters(FormatterRegistry registry) { 610 } 611 612 /** 613 * Return a global {@link Validator} instance for example for validating 614 * {@code @ModelAttribute} and {@code @RequestBody} method arguments. 615 * Delegates to {@link #getValidator()} first and if that returns {@code null} 616 * checks the classpath for the presence of a JSR-303 implementations 617 * before creating a {@code OptionalValidatorFactoryBean}.If a JSR-303 618 * implementation is not available, a no-op {@link Validator} is returned. 619 */ 620 @Bean 621 public Validator mvcValidator() { 622 Validator validator = getValidator(); 623 if (validator == null) { 624 if (ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) { 625 Class<?> clazz; 626 try { 627 String className = "org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean"; 628 clazz = ClassUtils.forName(className, WebMvcConfigurationSupport.class.getClassLoader()); 629 } 630 catch (ClassNotFoundException ex) { 631 throw new BeanInitializationException("Could not find default validator class", ex); 632 } 633 catch (LinkageError ex) { 634 throw new BeanInitializationException("Could not load default validator class", ex); 635 } 636 validator = (Validator) BeanUtils.instantiateClass(clazz); 637 } 638 else { 639 validator = new NoOpValidator(); 640 } 641 } 642 return validator; 643 } 644 645 /** 646 * Override this method to provide a custom {@link Validator}. 647 */ 648 protected Validator getValidator() { 649 return null; 650 } 651 652 /** 653 * Provide access to the shared custom argument resolvers used by the 654 * {@link RequestMappingHandlerAdapter} and the {@link ExceptionHandlerExceptionResolver}. 655 * <p>This method cannot be overridden; use {@link #addArgumentResolvers} instead. 656 * @since 4.3 657 */ 658 protected final List<HandlerMethodArgumentResolver> getArgumentResolvers() { 659 if (this.argumentResolvers == null) { 660 this.argumentResolvers = new ArrayList<HandlerMethodArgumentResolver>(); 661 addArgumentResolvers(this.argumentResolvers); 662 } 663 return this.argumentResolvers; 664 } 665 666 /** 667 * Add custom {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers} 668 * to use in addition to the ones registered by default. 669 * <p>Custom argument resolvers are invoked before built-in resolvers except for 670 * those that rely on the presence of annotations (e.g. {@code @RequestParameter}, 671 * {@code @PathVariable}, etc). The latter can be customized by configuring the 672 * {@link RequestMappingHandlerAdapter} directly. 673 * @param argumentResolvers the list of custom converters (initially an empty list) 674 */ 675 protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { 676 } 677 678 /** 679 * Provide access to the shared return value handlers used by the 680 * {@link RequestMappingHandlerAdapter} and the {@link ExceptionHandlerExceptionResolver}. 681 * <p>This method cannot be overridden; use {@link #addReturnValueHandlers} instead. 682 * @since 4.3 683 */ 684 protected final List<HandlerMethodReturnValueHandler> getReturnValueHandlers() { 685 if (this.returnValueHandlers == null) { 686 this.returnValueHandlers = new ArrayList<HandlerMethodReturnValueHandler>(); 687 addReturnValueHandlers(this.returnValueHandlers); 688 } 689 return this.returnValueHandlers; 690 } 691 692 /** 693 * Add custom {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers} 694 * in addition to the ones registered by default. 695 * <p>Custom return value handlers are invoked before built-in ones except for 696 * those that rely on the presence of annotations (e.g. {@code @ResponseBody}, 697 * {@code @ModelAttribute}, etc). The latter can be customized by configuring the 698 * {@link RequestMappingHandlerAdapter} directly. 699 * @param returnValueHandlers the list of custom handlers (initially an empty list) 700 */ 701 protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { 702 } 703 704 /** 705 * Provides access to the shared {@link HttpMessageConverter HttpMessageConverters} 706 * used by the {@link RequestMappingHandlerAdapter} and the 707 * {@link ExceptionHandlerExceptionResolver}. 708 * <p>This method cannot be overridden; use {@link #configureMessageConverters} instead. 709 * Also see {@link #addDefaultHttpMessageConverters} for adding default message converters. 710 */ 711 protected final List<HttpMessageConverter<?>> getMessageConverters() { 712 if (this.messageConverters == null) { 713 this.messageConverters = new ArrayList<HttpMessageConverter<?>>(); 714 configureMessageConverters(this.messageConverters); 715 if (this.messageConverters.isEmpty()) { 716 addDefaultHttpMessageConverters(this.messageConverters); 717 } 718 extendMessageConverters(this.messageConverters); 719 } 720 return this.messageConverters; 721 } 722 723 /** 724 * Override this method to add custom {@link HttpMessageConverter HttpMessageConverters} 725 * to use with the {@link RequestMappingHandlerAdapter} and the 726 * {@link ExceptionHandlerExceptionResolver}. 727 * <p>Adding converters to the list turns off the default converters that would 728 * otherwise be registered by default. Also see {@link #addDefaultHttpMessageConverters} 729 * for adding default message converters. 730 * @param converters a list to add message converters to (initially an empty list) 731 */ 732 protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) { 733 } 734 735 /** 736 * Override this method to extend or modify the list of converters after it has 737 * been configured. This may be useful for example to allow default converters 738 * to be registered and then insert a custom converter through this method. 739 * @param converters the list of configured converters to extend 740 * @since 4.1.3 741 */ 742 protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { 743 } 744 745 /** 746 * Adds a set of default HttpMessageConverter instances to the given list. 747 * Subclasses can call this method from {@link #configureMessageConverters}. 748 * @param messageConverters the list to add the default message converters to 749 */ 750 protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) { 751 StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(); 752 stringConverter.setWriteAcceptCharset(false); 753 754 messageConverters.add(new ByteArrayHttpMessageConverter()); 755 messageConverters.add(stringConverter); 756 messageConverters.add(new ResourceHttpMessageConverter()); 757 messageConverters.add(new SourceHttpMessageConverter<Source>()); 758 messageConverters.add(new AllEncompassingFormHttpMessageConverter()); 759 760 if (romePresent) { 761 messageConverters.add(new AtomFeedHttpMessageConverter()); 762 messageConverters.add(new RssChannelHttpMessageConverter()); 763 } 764 765 if (jackson2XmlPresent) { 766 messageConverters.add(new MappingJackson2XmlHttpMessageConverter( 767 Jackson2ObjectMapperBuilder.xml().applicationContext(this.applicationContext).build())); 768 } 769 else if (jaxb2Present) { 770 messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); 771 } 772 773 if (jackson2Present) { 774 messageConverters.add(new MappingJackson2HttpMessageConverter( 775 Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build())); 776 } 777 else if (gsonPresent) { 778 messageConverters.add(new GsonHttpMessageConverter()); 779 } 780 } 781 782 /** 783 * Return an instance of {@link CompositeUriComponentsContributor} for use with 784 * {@link org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder}. 785 * @since 4.0 786 */ 787 @Bean 788 public CompositeUriComponentsContributor mvcUriComponentsContributor() { 789 return new CompositeUriComponentsContributor( 790 requestMappingHandlerAdapter().getArgumentResolvers(), mvcConversionService()); 791 } 792 793 /** 794 * Returns a {@link HttpRequestHandlerAdapter} for processing requests 795 * with {@link HttpRequestHandler}s. 796 */ 797 @Bean 798 public HttpRequestHandlerAdapter httpRequestHandlerAdapter() { 799 return new HttpRequestHandlerAdapter(); 800 } 801 802 /** 803 * Returns a {@link SimpleControllerHandlerAdapter} for processing requests 804 * with interface-based controllers. 805 */ 806 @Bean 807 public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() { 808 return new SimpleControllerHandlerAdapter(); 809 } 810 811 /** 812 * Returns a {@link HandlerExceptionResolverComposite} containing a list of exception 813 * resolvers obtained either through {@link #configureHandlerExceptionResolvers} or 814 * through {@link #addDefaultHandlerExceptionResolvers}. 815 * <p><strong>Note:</strong> This method cannot be made final due to CGLIB constraints. 816 * Rather than overriding it, consider overriding {@link #configureHandlerExceptionResolvers} 817 * which allows for providing a list of resolvers. 818 */ 819 @Bean 820 public HandlerExceptionResolver handlerExceptionResolver() { 821 List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<HandlerExceptionResolver>(); 822 configureHandlerExceptionResolvers(exceptionResolvers); 823 if (exceptionResolvers.isEmpty()) { 824 addDefaultHandlerExceptionResolvers(exceptionResolvers); 825 } 826 extendHandlerExceptionResolvers(exceptionResolvers); 827 HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite(); 828 composite.setOrder(0); 829 composite.setExceptionResolvers(exceptionResolvers); 830 return composite; 831 } 832 833 /** 834 * Override this method to configure the list of 835 * {@link HandlerExceptionResolver HandlerExceptionResolvers} to use. 836 * <p>Adding resolvers to the list turns off the default resolvers that would otherwise 837 * be registered by default. Also see {@link #addDefaultHandlerExceptionResolvers} 838 * that can be used to add the default exception resolvers. 839 * @param exceptionResolvers a list to add exception resolvers to (initially an empty list) 840 */ 841 protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { 842 } 843 844 /** 845 * Override this method to extend or modify the list of 846 * {@link HandlerExceptionResolver HandlerExceptionResolvers} after it has been configured. 847 * <p>This may be useful for example to allow default resolvers to be registered 848 * and then insert a custom one through this method. 849 * @param exceptionResolvers the list of configured resolvers to extend. 850 * @since 4.3 851 */ 852 protected void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { 853 } 854 855 /** 856 * A method available to subclasses for adding default 857 * {@link HandlerExceptionResolver HandlerExceptionResolvers}. 858 * <p>Adds the following exception resolvers: 859 * <ul> 860 * <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through 861 * {@link org.springframework.web.bind.annotation.ExceptionHandler} methods. 862 * <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with 863 * {@link org.springframework.web.bind.annotation.ResponseStatus}. 864 * <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring exception types 865 * </ul> 866 */ 867 protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { 868 ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver(); 869 exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager()); 870 exceptionHandlerResolver.setMessageConverters(getMessageConverters()); 871 exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers()); 872 exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers()); 873 if (jackson2Present) { 874 exceptionHandlerResolver.setResponseBodyAdvice( 875 Collections.<ResponseBodyAdvice<?>>singletonList(new JsonViewResponseBodyAdvice())); 876 } 877 exceptionHandlerResolver.setApplicationContext(this.applicationContext); 878 exceptionHandlerResolver.afterPropertiesSet(); 879 exceptionResolvers.add(exceptionHandlerResolver); 880 881 ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver(); 882 responseStatusResolver.setMessageSource(this.applicationContext); 883 exceptionResolvers.add(responseStatusResolver); 884 885 exceptionResolvers.add(new DefaultHandlerExceptionResolver()); 886 } 887 888 /** 889 * Protected method for plugging in a custom subclass of 890 * {@link ExceptionHandlerExceptionResolver}. 891 * @since 4.3 892 */ 893 protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() { 894 return new ExceptionHandlerExceptionResolver(); 895 } 896 897 /** 898 * Register a {@link ViewResolverComposite} that contains a chain of view resolvers 899 * to use for view resolution. 900 * By default this resolver is ordered at 0 unless content negotiation view 901 * resolution is used in which case the order is raised to 902 * {@link org.springframework.core.Ordered#HIGHEST_PRECEDENCE 903 * Ordered.HIGHEST_PRECEDENCE}. 904 * <p>If no other resolvers are configured, 905 * {@link ViewResolverComposite#resolveViewName(String, Locale)} returns null in order 906 * to allow other potential {@link ViewResolver} beans to resolve views. 907 * @since 4.1 908 */ 909 @Bean 910 public ViewResolver mvcViewResolver() { 911 ViewResolverRegistry registry = new ViewResolverRegistry( 912 mvcContentNegotiationManager(), this.applicationContext); 913 configureViewResolvers(registry); 914 915 if (registry.getViewResolvers().isEmpty()) { 916 String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( 917 this.applicationContext, ViewResolver.class, true, false); 918 if (names.length == 1) { 919 registry.getViewResolvers().add(new InternalResourceViewResolver()); 920 } 921 } 922 923 ViewResolverComposite composite = new ViewResolverComposite(); 924 composite.setOrder(registry.getOrder()); 925 composite.setViewResolvers(registry.getViewResolvers()); 926 composite.setApplicationContext(this.applicationContext); 927 composite.setServletContext(this.servletContext); 928 return composite; 929 } 930 931 /** 932 * Override this method to configure view resolution. 933 * @see ViewResolverRegistry 934 */ 935 protected void configureViewResolvers(ViewResolverRegistry registry) { 936 } 937 938 /** 939 * Return the registered {@link CorsConfiguration} objects, 940 * keyed by path pattern. 941 * @since 4.2 942 */ 943 protected final Map<String, CorsConfiguration> getCorsConfigurations() { 944 if (this.corsConfigurations == null) { 945 CorsRegistry registry = new CorsRegistry(); 946 addCorsMappings(registry); 947 this.corsConfigurations = registry.getCorsConfigurations(); 948 } 949 return this.corsConfigurations; 950 } 951 952 /** 953 * Override this method to configure cross origin requests processing. 954 * @since 4.2 955 * @see CorsRegistry 956 */ 957 protected void addCorsMappings(CorsRegistry registry) { 958 } 959 960 @Bean 961 @Lazy 962 public HandlerMappingIntrospector mvcHandlerMappingIntrospector() { 963 return new HandlerMappingIntrospector(); 964 } 965 966 967 968 private static final class EmptyHandlerMapping extends AbstractHandlerMapping { 969 970 @Override 971 protected Object getHandlerInternal(HttpServletRequest request) { 972 return null; 973 } 974 } 975 976 977 private static final class NoOpValidator implements Validator { 978 979 @Override 980 public boolean supports(Class<?> clazz) { 981 return false; 982 } 983 984 @Override 985 public void validate(Object target, Errors errors) { 986 } 987 } 988 989}