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