zoukankan      html  css  js  c++  java
  • SpringBoot(十)----SpringMVC自动配置&扩展配置

    1.SpringMVC自动配置

    SpringBoot 为我们定义好了SpringMVC的配置文件(org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.class),首先我们来参考一下官网的SpringMVC的自动配置的介绍。

    官网网址:https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-developing-web-applications.html#boot-features-spring-mvc-static-content

    官网上介绍SpringMVC自动配置是如下这么介绍的:

    他具体说了哪些内容?

    1.包括了ContentNegotiatingViewResolver和BeanNameViewResolver这两个bean。

    我们可以看一下这两个Bean的源码,在包org.springframework.web.servlet.view(ContentNegotiatingViewResolver)和org.springframework.web.servlet.view(BeanNameViewResolver)下。

    在ContentNegotiatingViewResolver.java中有这么一段代码:

    @Override
    protected void initServletContext(ServletContext servletContext) {
    		Collection<ViewResolver> matchingBeans =
    				BeanFactoryUtils.beansOfTypeIncludingAncestors(getApplicationContext(), ViewResolver.class).values();
    		if (this.viewResolvers == null) {
    			this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.size());
    			for (ViewResolver viewResolver : matchingBeans) {
    				if (this != viewResolver) {
    					this.viewResolvers.add(viewResolver);
    				}
    			}
    		}
    		else {
    			for (int i = 0; i < viewResolvers.size(); i++) {
    				if (matchingBeans.contains(viewResolvers.get(i))) {
    					continue;
    				}
    				String name = viewResolvers.get(i).getClass().getName() + i;
    				getApplicationContext().getAutowireCapableBeanFactory().initializeBean(viewResolvers.get(i), name);
    			}
    
    		}
    		if (this.viewResolvers.isEmpty()) {
    			logger.warn("Did not find any ViewResolvers to delegate to; please configure them using the " +
    					"'viewResolvers' property on the ContentNegotiatingViewResolver");
    		}
    		AnnotationAwareOrderComparator.sort(this.viewResolvers);
    		this.cnmFactoryBean.setServletContext(servletContext);
    	}
    

    所以这个类的作用就是组合所有的视图解析器,自动配置ViewResovler(视图解析器作用,根据方法返回值得到视图对象view)

    在此文件中,可以看到resolveViewName方法,里面代码是从this.viewResolvers获取候选的视图解析器,遍历容器里所有视图,然后通过如图所标记的获取候选视图的方法,获取候选的视图列表,再通过getBestView获取最合适的视图。

    @Override
    public View resolveViewName(String viewName, Locale locale) throws Exception {
    		RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
    		Assert.isInstanceOf(ServletRequestAttributes.class, attrs);
    		List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
    		if (requestedMediaTypes != null) {
    			List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
    			View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
    			if (bestView != null) {
    				return bestView;
    			}
    		}
    		if (this.useNotAcceptableStatusCode) {
    			if (logger.isDebugEnabled()) {
    				logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code");
    			}
    			return NOT_ACCEPTABLE_VIEW;
    		}
    		else {
    			logger.debug("No acceptable view found; returning null");
    			return null;
    		}
    	}
    

    遍历所有的视图解析器对象,从视图解析器里获取候选的视图,封装成list保存

    private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes)
    			throws Exception {
    
    		List<View> candidateViews = new ArrayList<View>();
    		for (ViewResolver viewResolver : this.viewResolvers) {
    			View view = viewResolver.resolveViewName(viewName, locale);
    			if (view != null) {
    				candidateViews.add(view);
    			}
    			for (MediaType requestedMediaType : requestedMediaTypes) {
    				List<String> extensions = this.contentNegotiationManager.resolveFileExtensions(requestedMediaType);
    				for (String extension : extensions) {
    					String viewNameWithExtension = viewName + '.' + extension;
    					view = viewResolver.resolveViewName(viewNameWithExtension, locale);
    					if (view != null) {
    						candidateViews.add(view);
    					}
    				}
    			}
    		}
    		if (!CollectionUtils.isEmpty(this.defaultViews)) {
    			candidateViews.addAll(this.defaultViews);
    		}
    		return candidateViews;
    	}
    

    我们只需要将视图解析器丢到Spring容器里,就可以加载到。

    添加视图解析器。

    package com.zk.myspringboot;
    
    import java.util.Locale;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.servlet.View;
    import org.springframework.web.servlet.ViewResolver;
    
    @SpringBootApplication
    public class SpringBootApplicationFirst {
    	public static void main(String[]args){
            SpringApplication.run(SpringBootApplicationFirst.class, args);
            }
    	@Bean
    	public ViewResolver myViewResolver(){
    		return new myViewResolver();	
    	}
    	private static class myViewResolver implements ViewResolver{
    
    		@Override
    		public View resolveViewName(String string, Locale locale) throws Exception {
    			// TODO Auto-generated method stub
    			return null;
    		}
    		
    	}
    }

     DispatcherServlet(org.springframework.web.servlet.DispatcherServlet.class)是Spring核心分发器,找到doDispatch方法,debug,可以看到加的视图解析器加载到了 

     

    2.支持服务静态资源,包括支持WebJars。

    这个具体怎么支持,我们在之前的博客中已经说明了,具体几个添加静态资源的路径。

    3.自动注册Converter, GenericConverter,和 Formatter bean。

    看一下这三个转换器的声明:
    Converter.java
    /*
     * Copyright 2002-2015 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.core.convert.converter;
    
    /**
     * A converter converts a source object of type {@code S} to a target of type {@code T}.
     *
     * <p>Implementations of this interface are thread-safe and can be shared.
     *
     * <p>Implementations may additionally implement {@link ConditionalConverter}.
     *
     * @author Keith Donald
     * @since 3.0
     * @param <S> the source type
     * @param <T> the target type
     */
    public interface Converter<S, T> {
    
    	/**
    	 * Convert the source object of type {@code S} to target type {@code T}.
    	 * @param source the source object to convert, which must be an instance of {@code S} (never {@code null})
    	 * @return the converted object, which must be an instance of {@code T} (potentially {@code null})
    	 * @throws IllegalArgumentException if the source cannot be converted to the desired target type
    	 */
    	T convert(S source);
    
    }
    

     GenericConverter.java

    /*
     * Copyright 2002-2015 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.core.convert.converter;
    
    import java.util.Set;
    
    import org.springframework.core.convert.TypeDescriptor;
    import org.springframework.util.Assert;
    
    /**
     * Generic converter interface for converting between two or more types.
     *
     * <p>This is the most flexible of the Converter SPI interfaces, but also the most complex.
     * It is flexible in that a GenericConverter may support converting between multiple source/target
     * type pairs (see {@link #getConvertibleTypes()}. In addition, GenericConverter implementations
     * have access to source/target {@link TypeDescriptor field context} during the type conversion
     * process. This allows for resolving source and target field metadata such as annotations and
     * generics information, which can be used to influence the conversion logic.
     *
     * <p>This interface should generally not be used when the simpler {@link Converter} or
     * {@link ConverterFactory} interface is sufficient.
     *
     * <p>Implementations may additionally implement {@link ConditionalConverter}.
     *
     * @author Keith Donald
     * @author Juergen Hoeller
     * @since 3.0
     * @see TypeDescriptor
     * @see Converter
     * @see ConverterFactory
     * @see ConditionalConverter
     */
    public interface GenericConverter {
    
    	/**
    	 * Return the source and target types that this converter can convert between.
    	 * <p>Each entry is a convertible source-to-target type pair.
    	 * <p>For {@link ConditionalConverter conditional converters} this method may return
    	 * {@code null} to indicate all source-to-target pairs should be considered.
    	 */
    	Set<ConvertiblePair> getConvertibleTypes();
    
    	/**
    	 * Convert the source object to the targetType described by the {@code TypeDescriptor}.
    	 * @param source the source object to convert (may be {@code null})
    	 * @param sourceType the type descriptor of the field we are converting from
    	 * @param targetType the type descriptor of the field we are converting to
    	 * @return the converted object
    	 */
    	Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
    
    
    	/**
    	 * Holder for a source-to-target class pair.
    	 */
    	final class ConvertiblePair {
    
    		private final Class<?> sourceType;
    
    		private final Class<?> targetType;
    
    		/**
    		 * Create a new source-to-target pair.
    		 * @param sourceType the source type
    		 * @param targetType the target type
    		 */
    		public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
    			Assert.notNull(sourceType, "Source type must not be null");
    			Assert.notNull(targetType, "Target type must not be null");
    			this.sourceType = sourceType;
    			this.targetType = targetType;
    		}
    
    		public Class<?> getSourceType() {
    			return this.sourceType;
    		}
    
    		public Class<?> getTargetType() {
    			return this.targetType;
    		}
    
    		@Override
    		public boolean equals(Object other) {
    			if (this == other) {
    				return true;
    			}
    			if (other == null || other.getClass() != ConvertiblePair.class) {
    				return false;
    			}
    			ConvertiblePair otherPair = (ConvertiblePair) other;
    			return (this.sourceType == otherPair.sourceType && this.targetType == otherPair.targetType);
    		}
    
    		@Override
    		public int hashCode() {
    			return (this.sourceType.hashCode() * 31 + this.targetType.hashCode());
    		}
    
    		@Override
    		public String toString() {
    			return (this.sourceType.getName() + " -> " + this.targetType.getName());
    		}
    	}
    
    }
    

    Formatter.java

    /*
     * Copyright 2002-2012 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.format;
    
    /**
     * Formats objects of type T.
     * A Formatter is both a Printer <i>and</i> a Parser for an object type.
     *
     * @author Keith Donald
     * @since 3.0
     * @param <T> the type of object this Formatter formats
     */
    public interface Formatter<T> extends Printer<T>, Parser<T> {
    
    }
    

    这是三个格式转换器的声明,主要作用是类型间的转换。

    4.支持httpMessageConverter。

    这个类是SpringMVC用来转换Http请求和响应的;HttpMessageConverters 是从容器中确定的;获取所有的HttpMessageConverter;如果想自定义给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中就可以了(怎么注册:用@Bean,@Component)

    HttpMessageConverters.java

    /*
     * Copyright 2012-2015 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.boot.autoconfigure.web;
    
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.Collections;
    import java.util.Iterator;
    import java.util.List;
    
    import org.springframework.http.converter.FormHttpMessageConverter;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
    import org.springframework.http.converter.xml.AbstractXmlHttpMessageConverter;
    import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
    import org.springframework.util.ClassUtils;
    import org.springframework.util.ReflectionUtils;
    import org.springframework.web.client.RestTemplate;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    
    /**
     * Bean used to manage the {@link HttpMessageConverter}s used in a Spring Boot
     * application. Provides a convenient way to add and merge additional
     * {@link HttpMessageConverter}s to a web application.
     * <p>
     * An instance of this bean can be registered with specific
     * {@link #HttpMessageConverters(HttpMessageConverter...) additional converters} if
     * needed, otherwise default converters will be used.
     * <p>
     * NOTE: The default converters used are the same as standard Spring MVC (see
     * {@link WebMvcConfigurationSupport#getMessageConverters} with some slight re-ordering to
     * put XML converters at the back of the list.
     *
     * @author Dave Syer
     * @author Phillip Webb
     * @author Andy Wilkinson
     * @see #HttpMessageConverters(HttpMessageConverter...)
     * @see #HttpMessageConverters(Collection)
     * @see #getConverters()
     */
    public class HttpMessageConverters implements Iterable<HttpMessageConverter<?>> {
    
    	private static final List<Class<?>> NON_REPLACING_CONVERTERS;
    
    	static {
    		List<Class<?>> nonReplacingConverters = new ArrayList<Class<?>>();
    		addClassIfExists(nonReplacingConverters, "org.springframework.hateoas.mvc."
    				+ "TypeConstrainedMappingJackson2HttpMessageConverter");
    		NON_REPLACING_CONVERTERS = Collections.unmodifiableList(nonReplacingConverters);
    	}
    
    	private final List<HttpMessageConverter<?>> converters;
    
    	/**
    	 * Create a new {@link HttpMessageConverters} instance with the specified additional
    	 * converters.
    	 * @param additionalConverters additional converters to be added. New converters will
    	 * be added to the front of the list, overrides will replace existing items without
    	 * changing the order. The {@link #getConverters()} methods can be used for further
    	 * converter manipulation.
    	 */
    	public HttpMessageConverters(HttpMessageConverter<?>... additionalConverters) {
    		this(Arrays.asList(additionalConverters));
    	}
    
    	/**
    	 * Create a new {@link HttpMessageConverters} instance with the specified additional
    	 * converters.
    	 * @param additionalConverters additional converters to be added. Items are added just
    	 * before any default converter of the same type (or at the front of the list if no
    	 * default converter is found) The {@link #postProcessConverters(List)} method can be
    	 * used for further converter manipulation.
    	 */
    	public HttpMessageConverters(
    			Collection<HttpMessageConverter<?>> additionalConverters) {
    		this(true, additionalConverters);
    	}
    
    	/**
    	 * Create a new {@link HttpMessageConverters} instance with the specified converters.
    	 * @param addDefaultConverters if default converters should be added
    	 * @param converters converters to be added. Items are added just before any default
    	 * converter of the same type (or at the front of the list if no default converter is
    	 * found) The {@link #postProcessConverters(List)} method can be used for further
    	 * converter manipulation.
    	 */
    	public HttpMessageConverters(boolean addDefaultConverters,
    			Collection<HttpMessageConverter<?>> converters) {
    		List<HttpMessageConverter<?>> combined = getCombinedConverters(converters,
    				addDefaultConverters ? getDefaultConverters()
    						: Collections.<HttpMessageConverter<?>>emptyList());
    		combined = postProcessConverters(combined);
    		this.converters = Collections.unmodifiableList(combined);
    	}
    
    	private List<HttpMessageConverter<?>> getCombinedConverters(
    			Collection<HttpMessageConverter<?>> converters,
    			List<HttpMessageConverter<?>> defaultConverters) {
    		List<HttpMessageConverter<?>> combined = new ArrayList<HttpMessageConverter<?>>();
    		List<HttpMessageConverter<?>> processing = new ArrayList<HttpMessageConverter<?>>(
    				converters);
    		for (HttpMessageConverter<?> defaultConverter : defaultConverters) {
    			Iterator<HttpMessageConverter<?>> iterator = processing.iterator();
    			while (iterator.hasNext()) {
    				HttpMessageConverter<?> candidate = iterator.next();
    				if (isReplacement(defaultConverter, candidate)) {
    					combined.add(candidate);
    					iterator.remove();
    				}
    			}
    			combined.add(defaultConverter);
    			if (defaultConverter instanceof AllEncompassingFormHttpMessageConverter) {
    				configurePartConverters(
    						(AllEncompassingFormHttpMessageConverter) defaultConverter,
    						converters);
    			}
    		}
    		combined.addAll(0, processing);
    		return combined;
    	}
    
    	private boolean isReplacement(HttpMessageConverter<?> defaultConverter,
    			HttpMessageConverter<?> candidate) {
    		for (Class<?> nonReplacingConverter : NON_REPLACING_CONVERTERS) {
    			if (nonReplacingConverter.isInstance(candidate)) {
    				return false;
    			}
    		}
    		return ClassUtils.isAssignableValue(defaultConverter.getClass(), candidate);
    	}
    
    	private void configurePartConverters(
    			AllEncompassingFormHttpMessageConverter formConverter,
    			Collection<HttpMessageConverter<?>> converters) {
    		List<HttpMessageConverter<?>> partConverters = extractPartConverters(
    				formConverter);
    		List<HttpMessageConverter<?>> combinedConverters = getCombinedConverters(
    				converters, partConverters);
    		combinedConverters = postProcessPartConverters(combinedConverters);
    		formConverter.setPartConverters(combinedConverters);
    	}
    
    	@SuppressWarnings("unchecked")
    	private List<HttpMessageConverter<?>> extractPartConverters(
    			FormHttpMessageConverter formConverter) {
    		Field field = ReflectionUtils.findField(FormHttpMessageConverter.class,
    				"partConverters");
    		ReflectionUtils.makeAccessible(field);
    		return (List<HttpMessageConverter<?>>) ReflectionUtils.getField(field,
    				formConverter);
    	}
    
    	/**
    	 * Method that can be used to post-process the {@link HttpMessageConverter} list
    	 * before it is used.
    	 * @param converters a mutable list of the converters that will be used.
    	 * @return the final converts list to use
    	 */
    	protected List<HttpMessageConverter<?>> postProcessConverters(
    			List<HttpMessageConverter<?>> converters) {
    		return converters;
    	}
    
    	/**
    	 * Method that can be used to post-process the {@link HttpMessageConverter} list
    	 * before it is used to configure the part converters of
    	 * {@link AllEncompassingFormHttpMessageConverter}.
    	 * @param converters a mutable list of the converters that will be used.
    	 * @return the final converts list to use
    	 * @since 1.3.0
    	 */
    	protected List<HttpMessageConverter<?>> postProcessPartConverters(
    			List<HttpMessageConverter<?>> converters) {
    		return converters;
    	}
    
    	private List<HttpMessageConverter<?>> getDefaultConverters() {
    		List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>();
    		if (ClassUtils.isPresent("org.springframework.web.servlet.config.annotation."
    				+ "WebMvcConfigurationSupport", null)) {
    			converters.addAll(new WebMvcConfigurationSupport() {
    				public List<HttpMessageConverter<?>> defaultMessageConverters() {
    					return super.getMessageConverters();
    				}
    			}.defaultMessageConverters());
    		}
    		else {
    			converters.addAll(new RestTemplate().getMessageConverters());
    		}
    		reorderXmlConvertersToEnd(converters);
    		return converters;
    	}
    
    	private void reorderXmlConvertersToEnd(List<HttpMessageConverter<?>> converters) {
    		List<HttpMessageConverter<?>> xml = new ArrayList<HttpMessageConverter<?>>();
    		for (Iterator<HttpMessageConverter<?>> iterator = converters.iterator(); iterator
    				.hasNext();) {
    			HttpMessageConverter<?> converter = iterator.next();
    			if ((converter instanceof AbstractXmlHttpMessageConverter)
    					|| (converter instanceof MappingJackson2XmlHttpMessageConverter)) {
    				xml.add(converter);
    				iterator.remove();
    			}
    		}
    		converters.addAll(xml);
    	}
    
    	@Override
    	public Iterator<HttpMessageConverter<?>> iterator() {
    		return getConverters().iterator();
    	}
    
    	/**
    	 * Return an immutable list of the converters in the order that they will be
    	 * registered.
    	 * @return the converters
    	 */
    	public List<HttpMessageConverter<?>> getConverters() {
    		return this.converters;
    	}
    
    	private static void addClassIfExists(List<Class<?>> list, String className) {
    		try {
    			list.add(Class.forName(className));
    		}
    		catch (ClassNotFoundException ex) {
    			// Ignore
    		}
    		catch (NoClassDefFoundError ex) {
    			// Ignore
    		}
    	}
    
    }
    

    5.自动注册MessageCodesResolver。

    Spring MVC有一个生成错误代码的策略,用于呈现来自错误消息:MessageCodesResolver。如果设置了spring.mvc.message-codes-resolver.format属性PREFIX_ERROR_CODE或POSTFIX_ERROR_CODE,则spring Boot会创建一个MessageCodesResolver。

    MessageCodesResolver.java

    /*
     * Copyright 2002-2012 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.validation;
    
    /**
     * Strategy interface for building message codes from validation error codes.
     * Used by DataBinder to build the codes list for ObjectErrors and FieldErrors.
     *
     * <p>The resulting message codes correspond to the codes of a
     * MessageSourceResolvable (as implemented by ObjectError and FieldError).
     *
     * @author Juergen Hoeller
     * @since 1.0.1
     * @see DataBinder#setMessageCodesResolver
     * @see ObjectError
     * @see FieldError
     * @see org.springframework.context.MessageSourceResolvable#getCodes()
     */
    public interface MessageCodesResolver {
    
    	/**
    	 * Build message codes for the given error code and object name.
    	 * Used for building the codes list of an ObjectError.
    	 * @param errorCode the error code used for rejecting the object
    	 * @param objectName the name of the object
    	 * @return the message codes to use
    	 */
    	String[] resolveMessageCodes(String errorCode, String objectName);
    
    	/**
    	 * Build message codes for the given error code and field specification.
    	 * Used for building the codes list of an FieldError.
    	 * @param errorCode the error code used for rejecting the value
    	 * @param objectName the name of the object
    	 * @param field the field name
    	 * @param fieldType the field type (may be {@code null} if not determinable)
    	 * @return the message codes to use
    	 */
    	String[] resolveMessageCodes(String errorCode, String objectName, String field, Class<?> fieldType);
    
    }
    

    6.静态index.html支持。

    7.自定义Favicon支持(图标支持)

    Spring Boot在配置的静态内容位置和类路径的根目录(按顺序)中查找favicon.ico。如果存在这样的文件,它将自动用作应用程序的favicon。

    8.自动使用ConfigurableWebBindingInitializer 

    Spring MVC使用WeBindinginitializer为特定请求初始化WebDataBinder。如果您创建自己的Configurablewebindinginitializer,Spring Boot会自动将Spring MVC配置为使用它。

    ConfigurableWebBindingInitializer.java

    /*
     * Copyright 2002-2012 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.web.bind.support;
    
    import org.springframework.beans.PropertyEditorRegistrar;
    import org.springframework.core.convert.ConversionService;
    import org.springframework.validation.BindingErrorProcessor;
    import org.springframework.validation.MessageCodesResolver;
    import org.springframework.validation.Validator;
    import org.springframework.web.bind.WebDataBinder;
    import org.springframework.web.context.request.WebRequest;
    
    /**
     * Convenient {@link WebBindingInitializer} for declarative configuration
     * in a Spring application context. Allows for reusing pre-configured
     * initializers with multiple controller/handlers.
     *
     * @author Juergen Hoeller
     * @since 2.5
     * @see #setDirectFieldAccess
     * @see #setMessageCodesResolver
     * @see #setBindingErrorProcessor
     * @see #setValidator(Validator)
     * @see #setConversionService(ConversionService)
     * @see #setPropertyEditorRegistrar
     */
    public class ConfigurableWebBindingInitializer implements WebBindingInitializer {
    
    	private boolean autoGrowNestedPaths = true;
    
    	private boolean directFieldAccess = false;
    
    	private MessageCodesResolver messageCodesResolver;
    
    	private BindingErrorProcessor bindingErrorProcessor;
    
    	private Validator validator;
    
    	private ConversionService conversionService;
    
    	private PropertyEditorRegistrar[] propertyEditorRegistrars;
    
    
    	/**
    	 * Set whether a binder should attempt to "auto-grow" a nested path that contains a null value.
    	 * <p>If "true", a null path location will be populated with a default object value and traversed
    	 * instead of resulting in an exception. This flag also enables auto-growth of collection elements
    	 * when accessing an out-of-bounds index.
    	 * <p>Default is "true" on a standard DataBinder. Note that this feature is only supported
    	 * for bean property access (DataBinder's default mode), not for field access.
    	 * @see org.springframework.validation.DataBinder#initBeanPropertyAccess()
    	 * @see org.springframework.validation.DataBinder#setAutoGrowNestedPaths
    	 */
    	public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) {
    		this.autoGrowNestedPaths = autoGrowNestedPaths;
    	}
    
    	/**
    	 * Return whether a binder should attempt to "auto-grow" a nested path that contains a null value.
    	 */
    	public boolean isAutoGrowNestedPaths() {
    		return this.autoGrowNestedPaths;
    	}
    
    	/**
    	 * Set whether to use direct field access instead of bean property access.
    	 * <p>Default is {@code false}, using bean property access.
    	 * Switch this to {@code true} in order to enforce direct field access.
    	 * @see org.springframework.validation.DataBinder#initDirectFieldAccess()
    	 * @see org.springframework.validation.DataBinder#initBeanPropertyAccess()
    	 */
    	public final void setDirectFieldAccess(boolean directFieldAccess) {
    		this.directFieldAccess = directFieldAccess;
    	}
    
    	/**
    	 * Return whether to use direct field access instead of bean property access.
    	 */
    	public boolean isDirectFieldAccess() {
    		return directFieldAccess;
    	}
    
    	/**
    	 * Set the strategy to use for resolving errors into message codes.
    	 * Applies the given strategy to all data binders used by this controller.
    	 * <p>Default is {@code null}, i.e. using the default strategy of
    	 * the data binder.
    	 * @see org.springframework.validation.DataBinder#setMessageCodesResolver
    	 */
    	public final void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) {
    		this.messageCodesResolver = messageCodesResolver;
    	}
    
    	/**
    	 * Return the strategy to use for resolving errors into message codes.
    	 */
    	public final MessageCodesResolver getMessageCodesResolver() {
    		return this.messageCodesResolver;
    	}
    
    	/**
    	 * Set the strategy to use for processing binding errors, that is,
    	 * required field errors and {@code PropertyAccessException}s.
    	 * <p>Default is {@code null}, that is, using the default strategy
    	 * of the data binder.
    	 * @see org.springframework.validation.DataBinder#setBindingErrorProcessor
    	 */
    	public final void setBindingErrorProcessor(BindingErrorProcessor bindingErrorProcessor) {
    		this.bindingErrorProcessor = bindingErrorProcessor;
    	}
    
    	/**
    	 * Return the strategy to use for processing binding errors.
    	 */
    	public final BindingErrorProcessor getBindingErrorProcessor() {
    		return this.bindingErrorProcessor;
    	}
    
    	/**
    	 * Set the Validator to apply after each binding step.
    	 */
    	public final void setValidator(Validator validator) {
    		this.validator = validator;
    	}
    
    	/**
    	 * Return the Validator to apply after each binding step, if any.
    	 */
    	public final Validator getValidator() {
    		return this.validator;
    	}
    
    	/**
    	 * Specify a ConversionService which will apply to every DataBinder.
    	 * @since 3.0
    	 */
    	public final void setConversionService(ConversionService conversionService) {
    		this.conversionService = conversionService;
    	}
    
    	/**
    	 * Return the ConversionService which will apply to every DataBinder.
    	 */
    	public final ConversionService getConversionService() {
    		return this.conversionService;
    	}
    
    	/**
    	 * Specify a single PropertyEditorRegistrar to be applied to every DataBinder.
    	 */
    	public final void setPropertyEditorRegistrar(PropertyEditorRegistrar propertyEditorRegistrar) {
    		this.propertyEditorRegistrars = new PropertyEditorRegistrar[] {propertyEditorRegistrar};
    	}
    
    	/**
    	 * Specify multiple PropertyEditorRegistrars to be applied to every DataBinder.
    	 */
    	public final void setPropertyEditorRegistrars(PropertyEditorRegistrar[] propertyEditorRegistrars) {
    		this.propertyEditorRegistrars = propertyEditorRegistrars;
    	}
    
    	/**
    	 * Return the PropertyEditorRegistrars to be applied to every DataBinder.
    	 */
    	public final PropertyEditorRegistrar[] getPropertyEditorRegistrars() {
    		return this.propertyEditorRegistrars;
    	}
    
    
    	@Override
    	public void initBinder(WebDataBinder binder, WebRequest request) {
    		binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
    		if (this.directFieldAccess) {
    			binder.initDirectFieldAccess();
    		}
    		if (this.messageCodesResolver != null) {
    			binder.setMessageCodesResolver(this.messageCodesResolver);
    		}
    		if (this.bindingErrorProcessor != null) {
    			binder.setBindingErrorProcessor(this.bindingErrorProcessor);
    		}
    		if (this.validator != null && binder.getTarget() != null &&
    				this.validator.supports(binder.getTarget().getClass())) {
    			binder.setValidator(this.validator);
    		}
    		if (this.conversionService != null) {
    			binder.setConversionService(this.conversionService);
    		}
    		if (this.propertyEditorRegistrars != null) {
    			for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
    				propertyEditorRegistrar.registerCustomEditors(binder);
    			}
    		}
    	}
    
    }
    

    2、扩展SpringMVC

    如果您希望保留Spring Boot MVC功能,并且希望添加其他MVC配置(拦截器、格式化程序、视图控制器和其他功能),则可以添加自己的@configuration类,类型为webmvcconfiguer,但不添加@EnableWebMvc。如果希望提供RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义实例,则可以声明WebMVCregistrationAdapter实例来提供此类组件。

    举个例子:

    我们继承WebMvcConfigurer方法,添加自己的方法MyWebMvcConfig

    MyWebMvcConfig.class

    package com.zk.myspringboot;
    import java.util.List;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.format.FormatterRegistry;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.validation.MessageCodesResolver;
    import org.springframework.validation.Validator;
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
    import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
    import org.springframework.web.servlet.HandlerExceptionResolver;
    import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
    import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
     
    /**
     * 静态资源映射
     */
    @Configuration
    public class MyWebMvcConfig implements WebMvcConfigurer {
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
          /*  registry.addResourceHandler("/test/**")
                    .addResourceLocations("classpath:/test/");*/
        }
    
    	@Override
    	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void addCorsMappings(CorsRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void addFormatters(FormatterRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void addInterceptors(InterceptorRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void addViewControllers(ViewControllerRegistry registry) {
    		// TODO Auto-generated method stub
    		registry.addViewController("/tang").setViewName("index");
    	}
    
    	@Override
    	public void configureAsyncSupport(AsyncSupportConfigurer arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void configureContentNegotiation(ContentNegotiationConfigurer arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void configureMessageConverters(List<HttpMessageConverter<?>> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void configurePathMatch(PathMatchConfigurer arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void configureViewResolvers(ViewResolverRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void extendMessageConverters(List<HttpMessageConverter<?>> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public MessageCodesResolver getMessageCodesResolver() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public Validator getValidator() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    }
    

     这里我只试验了一个方法

    @Override
    	public void addViewControllers(ViewControllerRegistry registry) {
    		// TODO Auto-generated method stub
    		registry.addViewController("/tang").setViewName("index");
    	}
    

      启动程序访问结果如下:

     

     它会访问到index.html页面

    3.全面接管SpringMVC

     我们在重写的MyWebMvcConfig.class中添加@EnableWebMvc注解时,可以使SpringMVC的自动配置类失效,例如:

    package com.zk.myspringboot;
    import java.util.List;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.format.FormatterRegistry;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.validation.MessageCodesResolver;
    import org.springframework.validation.Validator;
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
    import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
    import org.springframework.web.servlet.HandlerExceptionResolver;
    import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
    import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
     
    /**
     * 静态资源映射
     */
    @Configuration
    @EnableWebMvc
    public class MyWebMvcConfig implements WebMvcConfigurer {
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
          /*  registry.addResourceHandler("/test/**")
                    .addResourceLocations("classpath:/test/");*/
        }
    
    	@Override
    	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void addCorsMappings(CorsRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void addFormatters(FormatterRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void addInterceptors(InterceptorRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void addViewControllers(ViewControllerRegistry registry) {
    		// TODO Auto-generated method stub
    		registry.addViewController("/tang").setViewName("index");
    	}
    
    	@Override
    	public void configureAsyncSupport(AsyncSupportConfigurer arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void configureContentNegotiation(ContentNegotiationConfigurer arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void configureMessageConverters(List<HttpMessageConverter<?>> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void configurePathMatch(PathMatchConfigurer arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void configureViewResolvers(ViewResolverRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public void extendMessageConverters(List<HttpMessageConverter<?>> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    
    	@Override
    	public MessageCodesResolver getMessageCodesResolver() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public Validator getValidator() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    }

      我们可以跟一下@EnableWebMvc注解的方法

    /*
     * Copyright 2002-2016 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.web.servlet.config.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.context.annotation.Import;
    
    /**
     * Adding this annotation to an {@code @Configuration} class imports the Spring MVC
     * configuration from {@link WebMvcConfigurationSupport}, e.g.:
     *
     * <pre class="code">
     * @Configuration
     * @EnableWebMvc
     * @ComponentScan(basePackageClasses = { MyConfiguration.class })
     * public class MyWebConfiguration {
     *
     * }
     * </pre>
     *
     * <p>To customize the imported configuration, implement the interface
     * {@link WebMvcConfigurer} or more likely extend the empty method base class
     * {@link WebMvcConfigurerAdapter} and override individual methods, e.g.:
     *
     * <pre class="code">
     * @Configuration
     * @EnableWebMvc
     * @ComponentScan(basePackageClasses = { MyConfiguration.class })
     * public class MyConfiguration extends WebMvcConfigurerAdapter {
     *
     * 	   @Override
     * 	   public void addFormatters(FormatterRegistry formatterRegistry) {
     *         formatterRegistry.addConverter(new MyConverter());
     * 	   }
     *
     * 	   @Override
     * 	   public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
     *         converters.add(new MyHttpMessageConverter());
     * 	   }
     *
     *     // More overridden methods ...
     * }
     * </pre>
     *
     * <p>If {@link WebMvcConfigurer} does not expose some advanced setting that
     * needs to be configured, consider removing the {@code @EnableWebMvc}
     * annotation and extending directly from {@link WebMvcConfigurationSupport}
     * or {@link DelegatingWebMvcConfiguration}, e.g.:
     *
     * <pre class="code">
     * @Configuration
     * @ComponentScan(basePackageClasses = { MyConfiguration.class })
     * public class MyConfiguration extends WebMvcConfigurationSupport {
     *
     * 	   @Override
     *	   public void addFormatters(FormatterRegistry formatterRegistry) {
     *         formatterRegistry.addConverter(new MyConverter());
     *	   }
     *
     *	   @Bean
     *	   public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
     *         // Create or delegate to "super" to create and
     *         // customize properties of RequestMappingHandlerAdapter
     *	   }
     * }
     * </pre>
     *
     * @author Dave Syer
     * @author Rossen Stoyanchev
     * @since 3.1
     * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer
     * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
     * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
     * @see org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(DelegatingWebMvcConfiguration.class)
    public @interface EnableWebMvc {
    }
    

      可以发现在这个接口中导入了DelegatingWebMvcConfiguration.class类,我们跟一下这个类

    DelegatingWebMvcConfiguration.class

    /*
     * Copyright 2002-2016 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.web.servlet.config.annotation;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.format.FormatterRegistry;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.util.CollectionUtils;
    import org.springframework.validation.MessageCodesResolver;
    import org.springframework.validation.Validator;
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
    import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
    import org.springframework.web.servlet.HandlerExceptionResolver;
    
    /**
     * A subclass of {@code WebMvcConfigurationSupport} that detects and delegates
     * to all beans of type {@link WebMvcConfigurer} allowing them to customize the
     * configuration provided by {@code WebMvcConfigurationSupport}. This is the
     * class actually imported by {@link EnableWebMvc @EnableWebMvc}.
     *
     * @author Rossen Stoyanchev
     * @since 3.1
     */
    @Configuration
    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    
    	private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    
    
    	@Autowired(required = false)
    	public void setConfigurers(List<WebMvcConfigurer> configurers) {
    		if (!CollectionUtils.isEmpty(configurers)) {
    			this.configurers.addWebMvcConfigurers(configurers);
    		}
    	}
    
    
    	@Override
    	protected void configurePathMatch(PathMatchConfigurer configurer) {
    		this.configurers.configurePathMatch(configurer);
    	}
    
    	@Override
    	protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    		this.configurers.configureContentNegotiation(configurer);
    	}
    
    	@Override
    	protected void configureAsyncSupport(AsyncSupportConfigurer configurer) {
    		this.configurers.configureAsyncSupport(configurer);
    	}
    
    	@Override
    	protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    		this.configurers.configureDefaultServletHandling(configurer);
    	}
    
    	@Override
    	protected void addFormatters(FormatterRegistry registry) {
    		this.configurers.addFormatters(registry);
    	}
    
    	@Override
    	protected void addInterceptors(InterceptorRegistry registry) {
    		this.configurers.addInterceptors(registry);
    	}
    
    	@Override
    	protected void addResourceHandlers(ResourceHandlerRegistry registry) {
    		this.configurers.addResourceHandlers(registry);
    	}
    
    	@Override
    	protected void addCorsMappings(CorsRegistry registry) {
    		this.configurers.addCorsMappings(registry);
    	}
    
    	@Override
    	protected void addViewControllers(ViewControllerRegistry registry) {
    		this.configurers.addViewControllers(registry);
    	}
    
    	@Override
    	protected void configureViewResolvers(ViewResolverRegistry registry) {
    		this.configurers.configureViewResolvers(registry);
    	}
    
    	@Override
    	protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    		this.configurers.addArgumentResolvers(argumentResolvers);
    	}
    
    	@Override
    	protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
    		this.configurers.addReturnValueHandlers(returnValueHandlers);
    	}
    
    	@Override
    	protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    		this.configurers.configureMessageConverters(converters);
    	}
    
    	@Override
    	protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    		this.configurers.extendMessageConverters(converters);
    	}
    
    	@Override
    	protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    		this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);
    	}
    
    	@Override
    	protected void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
    		this.configurers.extendHandlerExceptionResolvers(exceptionResolvers);
    	}
    
    	@Override
    	protected Validator getValidator() {
    		return this.configurers.getValidator();
    	}
    
    	@Override
    	protected MessageCodesResolver getMessageCodesResolver() {
    		return this.configurers.getMessageCodesResolver();
    	}
    
    }
    

    可以发现这个类,它继承了WebMvcConfigurationSupport.class类

    然而在我们的WebMvcAutoConfiguration,存在注解@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),这个注解是当你未注入WebMvcConfigurationSupport.class累的时候,WebMvcAutoConfiguration类才会生效。

    WebMvcAutoConfiguration.java

    /*
     * Copyright 2012-2016 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.boot.autoconfigure.web;
    
    import java.util.Collection;
    import java.util.Collections;
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    
    import javax.servlet.Servlet;
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.beans.factory.BeanFactory;
    import org.springframework.beans.factory.ListableBeanFactory;
    import org.springframework.beans.factory.NoSuchBeanDefinitionException;
    import org.springframework.beans.factory.ObjectProvider;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.AutoConfigureAfter;
    import org.springframework.boot.autoconfigure.AutoConfigureOrder;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
    import org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter;
    import org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter;
    import org.springframework.boot.web.filter.OrderedRequestContextFilter;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.Primary;
    import org.springframework.core.Ordered;
    import org.springframework.core.convert.converter.Converter;
    import org.springframework.core.convert.converter.GenericConverter;
    import org.springframework.core.io.Resource;
    import org.springframework.format.Formatter;
    import org.springframework.format.FormatterRegistry;
    import org.springframework.format.datetime.DateFormatter;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.util.StringUtils;
    import org.springframework.validation.DefaultMessageCodesResolver;
    import org.springframework.validation.MessageCodesResolver;
    import org.springframework.web.accept.ContentNegotiationManager;
    import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
    import org.springframework.web.context.request.RequestContextListener;
    import org.springframework.web.filter.HiddenHttpMethodFilter;
    import org.springframework.web.filter.HttpPutFormContentFilter;
    import org.springframework.web.filter.RequestContextFilter;
    import org.springframework.web.servlet.DispatcherServlet;
    import org.springframework.web.servlet.HandlerExceptionResolver;
    import org.springframework.web.servlet.LocaleResolver;
    import org.springframework.web.servlet.View;
    import org.springframework.web.servlet.ViewResolver;
    import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
    import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
    import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.ResourceChainRegistration;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
    import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
    import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
    import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
    import org.springframework.web.servlet.i18n.FixedLocaleResolver;
    import org.springframework.web.servlet.mvc.ParameterizableViewController;
    import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
    import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
    import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
    import org.springframework.web.servlet.resource.AppCacheManifestTransformer;
    import org.springframework.web.servlet.resource.GzipResourceResolver;
    import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
    import org.springframework.web.servlet.resource.ResourceResolver;
    import org.springframework.web.servlet.resource.VersionResourceResolver;
    import org.springframework.web.servlet.view.BeanNameViewResolver;
    import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
    import org.springframework.web.servlet.view.InternalResourceViewResolver;
    
    /**
     * {@link EnableAutoConfiguration Auto-configuration} for {@link EnableWebMvc Web MVC}.
     *
     * @author Phillip Webb
     * @author Dave Syer
     * @author Andy Wilkinson
     * @author Stephane Nicoll
     */
    @Configuration
    @ConditionalOnWebApplication
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
    		WebMvcConfigurerAdapter.class })
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
    @AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
    public class WebMvcAutoConfiguration {
    
    	public static String DEFAULT_PREFIX = "";
    
    	public static String DEFAULT_SUFFIX = "";
    
    	@Bean
    	@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
    	public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
    		return new OrderedHiddenHttpMethodFilter();
    	}
    
    	@Bean
    	@ConditionalOnMissingBean(HttpPutFormContentFilter.class)
    	@ConditionalOnProperty(prefix = "spring.mvc.formcontent.putfilter", name = "enabled", matchIfMissing = true)
    	public OrderedHttpPutFormContentFilter httpPutFormContentFilter() {
    		return new OrderedHttpPutFormContentFilter();
    	}
    
    	// Defined as a nested config to ensure WebMvcConfigurerAdapter is not read when not
    	// on the classpath
    	@Configuration
    	@Import(EnableWebMvcConfiguration.class)
    	@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
    	public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {
    
    		private static final Log logger = LogFactory
    				.getLog(WebMvcConfigurerAdapter.class);
    
    		private final ResourceProperties resourceProperties;
    
    		private final WebMvcProperties mvcProperties;
    
    		private final ListableBeanFactory beanFactory;
    
    		private final HttpMessageConverters messageConverters;
    
    		final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
    
    		public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,
    				WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
    				HttpMessageConverters messageConverters,
    				ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
    			this.resourceProperties = resourceProperties;
    			this.mvcProperties = mvcProperties;
    			this.beanFactory = beanFactory;
    			this.messageConverters = messageConverters;
    			this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider
    					.getIfAvailable();
    		}
    
    		@Override
    		public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    			converters.addAll(this.messageConverters.getConverters());
    		}
    
    		@Override
    		public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
    			Long timeout = this.mvcProperties.getAsync().getRequestTimeout();
    			if (timeout != null) {
    				configurer.setDefaultTimeout(timeout);
    			}
    		}
    
    		@Override
    		public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    			Map<String, MediaType> mediaTypes = this.mvcProperties.getMediaTypes();
    			for (Entry<String, MediaType> mediaType : mediaTypes.entrySet()) {
    				configurer.mediaType(mediaType.getKey(), mediaType.getValue());
    			}
    		}
    
    		@Bean
    		@ConditionalOnMissingBean
    		public InternalResourceViewResolver defaultViewResolver() {
    			InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    			resolver.setPrefix(this.mvcProperties.getView().getPrefix());
    			resolver.setSuffix(this.mvcProperties.getView().getSuffix());
    			return resolver;
    		}
    
    		@Bean
    		@ConditionalOnBean(View.class)
    		@ConditionalOnMissingBean
    		public BeanNameViewResolver beanNameViewResolver() {
    			BeanNameViewResolver resolver = new BeanNameViewResolver();
    			resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
    			return resolver;
    		}
    
    		@Bean
    		@ConditionalOnBean(ViewResolver.class)
    		@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
    		public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
    			ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
    			resolver.setContentNegotiationManager(
    					beanFactory.getBean(ContentNegotiationManager.class));
    			// ContentNegotiatingViewResolver uses all the other view resolvers to locate
    			// a view so it should have a high precedence
    			resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
    			return resolver;
    		}
    
    		@Bean
    		@ConditionalOnMissingBean
    		@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
    		public LocaleResolver localeResolver() {
    			if (this.mvcProperties
    					.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
    				return new FixedLocaleResolver(this.mvcProperties.getLocale());
    			}
    			AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
    			localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
    			return localeResolver;
    		}
    
    		@Bean
    		@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
    		public Formatter<Date> dateFormatter() {
    			return new DateFormatter(this.mvcProperties.getDateFormat());
    		}
    
    		@Override
    		public MessageCodesResolver getMessageCodesResolver() {
    			if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
    				DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
    				resolver.setMessageCodeFormatter(
    						this.mvcProperties.getMessageCodesResolverFormat());
    				return resolver;
    			}
    			return null;
    		}
    
    		@Override
    		public void addFormatters(FormatterRegistry registry) {
    			for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
    				registry.addConverter(converter);
    			}
    			for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
    				registry.addConverter(converter);
    			}
    			for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
    				registry.addFormatter(formatter);
    			}
    		}
    
    		private <T> Collection<T> getBeansOfType(Class<T> type) {
    			return this.beanFactory.getBeansOfType(type).values();
    		}
    
    		@Override
    		public void addResourceHandlers(ResourceHandlerRegistry registry) {
    			if (!this.resourceProperties.isAddMappings()) {
    				logger.debug("Default resource handling disabled");
    				return;
    			}
    			Integer cachePeriod = this.resourceProperties.getCachePeriod();
    			if (!registry.hasMappingForPattern("/webjars/**")) {
    				customizeResourceHandlerRegistration(
    						registry.addResourceHandler("/webjars/**")
    								.addResourceLocations(
    										"classpath:/META-INF/resources/webjars/")
    						.setCachePeriod(cachePeriod));
    			}
    			String staticPathPattern = this.mvcProperties.getStaticPathPattern();
    			if (!registry.hasMappingForPattern(staticPathPattern)) {
    				customizeResourceHandlerRegistration(
    						registry.addResourceHandler(staticPathPattern)
    								.addResourceLocations(
    										this.resourceProperties.getStaticLocations())
    						.setCachePeriod(cachePeriod));
    			}
    		}
    
    		@Bean
    		public WelcomePageHandlerMapping welcomePageHandlerMapping(
    				ResourceProperties resourceProperties) {
    			return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage());
    		}
    
    		private void customizeResourceHandlerRegistration(
    				ResourceHandlerRegistration registration) {
    			if (this.resourceHandlerRegistrationCustomizer != null) {
    				this.resourceHandlerRegistrationCustomizer.customize(registration);
    			}
    
    		}
    
    		@Bean
    		@ConditionalOnMissingBean({ RequestContextListener.class,
    				RequestContextFilter.class })
    		public static RequestContextFilter requestContextFilter() {
    			return new OrderedRequestContextFilter();
    		}
    
    		@Configuration
    		@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
    		public static class FaviconConfiguration {
    
    			private final ResourceProperties resourceProperties;
    
    			public FaviconConfiguration(ResourceProperties resourceProperties) {
    				this.resourceProperties = resourceProperties;
    			}
    
    			@Bean
    			public SimpleUrlHandlerMapping faviconHandlerMapping() {
    				SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
    				mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
    				mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
    						faviconRequestHandler()));
    				return mapping;
    			}
    
    			@Bean
    			public ResourceHttpRequestHandler faviconRequestHandler() {
    				ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
    				requestHandler
    						.setLocations(this.resourceProperties.getFaviconLocations());
    				return requestHandler;
    			}
    
    		}
    
    	}
    
    	/**
    	 * Configuration equivalent to {@code @EnableWebMvc}.
    	 */
    	@Configuration
    	public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
    
    		private final WebMvcProperties mvcProperties;
    
    		private final ListableBeanFactory beanFactory;
    
    		private final WebMvcRegistrations mvcRegistrations;
    
    		public EnableWebMvcConfiguration(
    				ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
    				ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
    				ListableBeanFactory beanFactory) {
    			this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
    			this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
    			this.beanFactory = beanFactory;
    		}
    
    		@Bean
    		@Override
    		public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    			RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
    			adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null ? true
    					: this.mvcProperties.isIgnoreDefaultModelOnRedirect());
    			return adapter;
    		}
    
    		@Override
    		protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
    			if (this.mvcRegistrations != null
    					&& this.mvcRegistrations.getRequestMappingHandlerAdapter() != null) {
    				return this.mvcRegistrations.getRequestMappingHandlerAdapter();
    			}
    			return super.createRequestMappingHandlerAdapter();
    		}
    
    		@Bean
    		@Primary
    		@Override
    		public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    			// Must be @Primary for MvcUriComponentsBuilder to work
    			return super.requestMappingHandlerMapping();
    		}
    
    		@Override
    		protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
    			if (this.mvcRegistrations != null
    					&& this.mvcRegistrations.getRequestMappingHandlerMapping() != null) {
    				return this.mvcRegistrations.getRequestMappingHandlerMapping();
    			}
    			return super.createRequestMappingHandlerMapping();
    		}
    
    		@Override
    		protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
    			try {
    				return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
    			}
    			catch (NoSuchBeanDefinitionException ex) {
    				return super.getConfigurableWebBindingInitializer();
    			}
    		}
    
    		@Override
    		protected ExceptionHandlerExceptionResolver createExceptionHandlerExceptionResolver() {
    			if (this.mvcRegistrations != null && this.mvcRegistrations
    					.getExceptionHandlerExceptionResolver() != null) {
    				return this.mvcRegistrations.getExceptionHandlerExceptionResolver();
    			}
    			return super.createExceptionHandlerExceptionResolver();
    		}
    
    		@Override
    		protected void configureHandlerExceptionResolvers(
    				List<HandlerExceptionResolver> exceptionResolvers) {
    			super.configureHandlerExceptionResolvers(exceptionResolvers);
    			if (exceptionResolvers.isEmpty()) {
    				addDefaultHandlerExceptionResolvers(exceptionResolvers);
    			}
    			if (this.mvcProperties.isLogResolvedException()) {
    				for (HandlerExceptionResolver resolver : exceptionResolvers) {
    					if (resolver instanceof AbstractHandlerExceptionResolver) {
    						((AbstractHandlerExceptionResolver) resolver)
    								.setWarnLogCategory(resolver.getClass().getName());
    					}
    				}
    			}
    		}
    
    	}
    
    	@Configuration
    	@ConditionalOnEnabledResourceChain
    	static class ResourceChainCustomizerConfiguration {
    
    		@Bean
    		public ResourceChainResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer() {
    			return new ResourceChainResourceHandlerRegistrationCustomizer();
    		}
    
    	}
    
    	interface ResourceHandlerRegistrationCustomizer {
    
    		void customize(ResourceHandlerRegistration registration);
    
    	}
    
    	private static class ResourceChainResourceHandlerRegistrationCustomizer
    			implements ResourceHandlerRegistrationCustomizer {
    
    		@Autowired
    		private ResourceProperties resourceProperties = new ResourceProperties();
    
    		@Override
    		public void customize(ResourceHandlerRegistration registration) {
    			ResourceProperties.Chain properties = this.resourceProperties.getChain();
    			configureResourceChain(properties,
    					registration.resourceChain(properties.isCache()));
    		}
    
    		private void configureResourceChain(ResourceProperties.Chain properties,
    				ResourceChainRegistration chain) {
    			Strategy strategy = properties.getStrategy();
    			if (strategy.getFixed().isEnabled() || strategy.getContent().isEnabled()) {
    				chain.addResolver(getVersionResourceResolver(strategy));
    			}
    			if (properties.isGzipped()) {
    				chain.addResolver(new GzipResourceResolver());
    			}
    			if (properties.isHtmlApplicationCache()) {
    				chain.addTransformer(new AppCacheManifestTransformer());
    			}
    		}
    
    		private ResourceResolver getVersionResourceResolver(
    				ResourceProperties.Strategy properties) {
    			VersionResourceResolver resolver = new VersionResourceResolver();
    			if (properties.getFixed().isEnabled()) {
    				String version = properties.getFixed().getVersion();
    				String[] paths = properties.getFixed().getPaths();
    				resolver.addFixedVersionStrategy(version, paths);
    			}
    			if (properties.getContent().isEnabled()) {
    				String[] paths = properties.getContent().getPaths();
    				resolver.addContentVersionStrategy(paths);
    			}
    			return resolver;
    		}
    
    	}
    
    	static final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping {
    
    		private static final Log logger = LogFactory
    				.getLog(WelcomePageHandlerMapping.class);
    
    		private WelcomePageHandlerMapping(Resource welcomePage) {
    			if (welcomePage != null) {
    				logger.info("Adding welcome page: " + welcomePage);
    				ParameterizableViewController controller = new ParameterizableViewController();
    				controller.setViewName("forward:index.html");
    				setRootHandler(controller);
    				setOrder(0);
    			}
    		}
    
    		@Override
    		public Object getHandlerInternal(HttpServletRequest request) throws Exception {
    			for (MediaType mediaType : getAcceptedMediaTypes(request)) {
    				if (mediaType.includes(MediaType.TEXT_HTML)) {
    					return super.getHandlerInternal(request);
    				}
    			}
    			return null;
    		}
    
    		private List<MediaType> getAcceptedMediaTypes(HttpServletRequest request) {
    			String acceptHeader = request.getHeader(HttpHeaders.ACCEPT);
    			return MediaType.parseMediaTypes(
    					StringUtils.hasText(acceptHeader) ? acceptHeader : "*/*");
    		}
    
    	}
    
    }
    

    所以,当我们配置了@EnableWebMvc,就相当于导入了WebMvcConfigurationSupport,所以原始的SpringMVC自动配置类失效。@EnableWebMvc将WebMvcConfigurationSupport组件导入进来,导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能。

    4.修改SpringBoot的默认配置

    1.SpringBoot在自动配置很多组件的时候,先看一下容器中有没有用户自己配置的(@bean@Component),如果有就用用户配置的,如果没有才会自己配置,如果有多个组件可以有多个(viewresolver)将用户配置和自己的配置结合起来。

    2.在SpringBoot中会有很多的xxxConfigurer帮助我们进行扩展。

    3.在SpringBoot中会有很多的xxxCustomizer帮助我们定制。

    例如:

    package com.zk.myspringboot;
    
    import java.util.Locale;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.servlet.View;
    import org.springframework.web.servlet.ViewResolver;
    
    @SpringBootApplication
    public class SpringBootApplicationFirst {
    	public static void main(String[]args){
            SpringApplication.run(SpringBootApplicationFirst.class, args);
        }
    	@Bean
    	public ViewResolver myViewResolver(){
    		return new myViewResolver();	
    	}
    	@Bean
    	public ViewResolver myViewResolver01(){
    		return new myViewResolver01();	
    	}
    	private static class myViewResolver implements ViewResolver{
    
    		@Override
    		public View resolveViewName(String string, Locale locale) throws Exception {
    			// TODO Auto-generated method stub
    			return null;
    		}
    		
    	}
    	private static class myViewResolver01 implements ViewResolver{
    
    		@Override
    		public View resolveViewName(String string, Locale locale) throws Exception {
    			// TODO Auto-generated method stub
    			return null;
    		}
    		
    	}
    }
    

      打入debug模式可以在控制台中看到我们的viewResolvers中我们自定义的两个视图

  • 相关阅读:
    java多线程:并发包中ConcurrentHashMap和jdk的HashMap的对比
    java编程之:Unsafe类
    mave之:java的web项目必须要的三个jar的pom形式
    java多线程:并发包中ReentrantReadWriteLock读写锁的锁降级模板
    java多线程:并发包中ReentrantReadWriteLock读写锁的原理
    java编程之:按位与运算,等运算规则
    java多线程:jdk并发包的总结(转载)
    liunx之:wps for liunx的安装经验
    mysql中enum类型理解
    MySQL类型float double decimal的区别
  • 原文地址:https://www.cnblogs.com/longlyseul/p/12723493.html
Copyright © 2011-2022 走看看