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