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}