spring boot web应用的自动配置

web应用自动配置

在之前的文章中分析过,@SpringBootApplication会使用到@Import注解来引入AutoConfigurationImportSelector
AutoConfigurationImportSelector则会通过spi机制来返回需要加载的自动配置类
其中就包括DispatcherServletAutoConfiguration和WebMvcAutoConfiguration
前者用来进行servlet相关的配置,后者用来进行mvc相关组件的配置

DispatcherServletAutoConfiguration

首先看下这个类都使用了哪些注解

// 用来指定自动初始化的顺序
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
// 指定当前类为配置类
@Configuration(proxyBeanMethods = false)
// 当应用类型是servlet时才加载
@ConditionalOnWebApplication(type = Type.SERVLET)
// 类路径中有DispatcherServlet时才加载
@ConditionalOnClass(DispatcherServlet.class)
// 在ServletWebServerFactoryAutoConfiguration加载完成之后再加载
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
}

另外该类中存在如下几个内部类
在这里插入图片描述

DispatcherServletConfiguration

// 指定当前类为配置类
@Configuration(proxyBeanMethods = false)
// 满足DefaultDispatcherServletCondition时才进行加载
@Conditional(DefaultDispatcherServletCondition.class)
// 类路径中出现ServletRegistration才加载
@ConditionalOnClass(ServletRegistration.class)
// 使WebMvcProperties生效
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {

	// 初始化DispatcherSerlvet,bean的名称为dispatcherServlet
	@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
	public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
		DispatcherServlet dispatcherServlet = new DispatcherServlet();
		// 通过注入的webMvcProperties来配置dispatcherSerlvet
		dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
		dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
		dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
		dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
		dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
		return dispatcherServlet;
	}

	// 初始化MultipartResolver,用来上传文件
	// 这里的加载条件是,上下文中存在MultipartResolver的一个bean,但是该bean的名称不是multipartResolver
	// 这里会将这个bean的名称命名为multipartResolver
	@Bean
	@ConditionalOnBean(MultipartResolver.class)
	@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
	public MultipartResolver multipartResolver(MultipartResolver resolver) {
		// Detect if the user has created a MultipartResolver but named it incorrectly
		return resolver;
	}

}

下面看下WebMvcProperties这个类

@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
}

可以看到这个类主要用来接收配置中spring.mvc开头的配置

DefaultDispatcherServletCondition

接着看下用来代表DispatcherServletConfiguration加载条件的DefaultDispatcherServletCondition

@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DefaultDispatcherServletCondition extends SpringBootCondition {

	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		ConditionMessage.Builder message = ConditionMessage.forCondition("Default DispatcherServlet");
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		// 从beanFactory中获取类型为DispatcherServlet的bean的名称
		List<String> dispatchServletBeans = Arrays
				.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
		if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
			// 如果已经包含名称为dispatcherSerlet并且类型为DispatcherServlet的bean,那么此时不匹配
			return ConditionOutcome
					.noMatch(message.found("dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
		}
		if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
			// 如果已经包含名称为dispatcherSerlvet的bean,那么此时不匹配
			return ConditionOutcome.noMatch(
					message.found("non dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
		}
		if (dispatchServletBeans.isEmpty()) {
			// 当前不存在类型为DispatcherServlet的bean,那么匹配
			return ConditionOutcome.match(message.didNotFind("dispatcher servlet beans").atAll());
		}
		// 虽然有类型DispatcherServlet的bean,但是名称不为dispatcherServlet,仍然匹配通过
		return ConditionOutcome.match(message.found("dispatcher servlet bean", "dispatcher servlet beans")
				.items(Style.QUOTE, dispatchServletBeans)
				.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
	}

}

DispatcherServletRegistrationConfiguration

主要作用就是注册dispatcherServlet,并且配置一些属性

// 当前类是配置类
@Configuration(proxyBeanMethods = false)
// 满足DispatcherServletRegistrationCondition才加载
@Conditional(DispatcherServletRegistrationCondition.class)
// 类路径中有ServletRegistration才加载
@ConditionalOnClass(ServletRegistration.class)
// 开启WebMvcProperties的属性自动注入
@EnableConfigurationProperties(WebMvcProperties.class)
// 导入DispatcherServletConfiguration
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {

	// 返回名称为dispatcherServletRegistration 类型为dispatcherServletRegistration的bean
	@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
	// 当beanFactory中同时存在类型为DispatcherServlet和名称为dispatcherServlet的bean时才加载
	@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
	public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
			WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
		// 默认的path为/
		DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
				webMvcProperties.getServlet().getPath());
		registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
		registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
		// 设置上传文件方面的配置
		multipartConfig.ifAvailable(registration::setMultipartConfig);
		return registration;
	}

}
public class DispatcherServletRegistrationBean extends ServletRegistrationBean<DispatcherServlet>
		implements DispatcherServletPath {

	private final String path;

	/**
	 * Create a new {@link DispatcherServletRegistrationBean} instance for the given
	 * servlet and path.
	 * @param servlet the dispatcher servlet
	 * @param path the dispatcher servlet path
	 */
	public DispatcherServletRegistrationBean(DispatcherServlet servlet, String path) {
		super(servlet);
		Assert.notNull(path, "Path must not be null");
		this.path = path;
		// 添加urlMappings
		super.addUrlMappings(getServletUrlMapping());
	}

	@Override
	public String getPath() {
		return this.path;
	}
	
	// 不支持手动修改urlMapping
	@Override
	public void setUrlMappings(Collection<String> urlMappings) {
		throw new UnsupportedOperationException("URL Mapping cannot be changed on a DispatcherServlet registration");
	}

	@Override
	public void addUrlMappings(String... urlMappings) {
		throw new UnsupportedOperationException("URL Mapping cannot be changed on a DispatcherServlet registration");
	}

}

下面看下getServletUrlMapping

default String getServletUrlMapping() {
	if (getPath().equals("") || getPath().equals("/")) {
		return "/";
	}
	if (getPath().contains("*")) {
		return getPath();
	}
	if (getPath().endsWith("/")) {
		return getPath() + "*";
	}
	return getPath() + "/*";
}

DispatcherServletRegistrationCondition

这个类的主要作用是用来判断是否需要加载DispatcherServletRegistrationConfiguration

@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DispatcherServletRegistrationCondition extends SpringBootCondition {

	@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory);
		if (!outcome.isMatch()) {
			return outcome;
		}
		return checkServletRegistration(beanFactory);
	}

	private ConditionOutcome checkDefaultDispatcherName(ConfigurableListableBeanFactory beanFactory) {
		// 获取beanFactory中类型为DispatcherServlet的bean的名称
		List<String> servlets = Arrays
				.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
		// 判断是否存在名称为dispatcherServlet的bean
		boolean containsDispatcherBean = beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
		// 如果存在名称为dispatcherServlet但是类型不是DispatcherServlet的bean,那么不匹配
		if (containsDispatcherBean && !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
			return ConditionOutcome.noMatch(
					startMessage().found("non dispatcher servlet").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
		}
		// 否则返回匹配
		return ConditionOutcome.match();
	}

	private ConditionOutcome checkServletRegistration(ConfigurableListableBeanFactory beanFactory) {
		ConditionMessage.Builder message = startMessage();
		// 获取beanFactory中类型为ServletRegistrationBean的bean
		List<String> registrations = Arrays
				.asList(beanFactory.getBeanNamesForType(ServletRegistrationBean.class, false, false));
		// 判断是否存在名称为dispatcherServletRegistration的bean
		boolean containsDispatcherRegistrationBean = beanFactory
				.containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
		if (registrations.isEmpty()) {
			if (containsDispatcherRegistrationBean) {
				// 不存在类型为ServletRegistrationBean,但是存在名称为dispatcherServletRegistration的bean,那么不匹配
				return ConditionOutcome.noMatch(message.found("non servlet registration bean")
						.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
			}
			// 既不存在类型为ServletRegistrationBean也不存在名称为dispatcherServletRegistration的bean,那么匹配
			return ConditionOutcome.match(message.didNotFind("servlet registration bean").atAll());
		}
		if (registrations.contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) {
			// 存在名称为dispatcherServletRegistration并且类型为ServletRegistrationBean的bean,那么不匹配
			return ConditionOutcome.noMatch(message.found("servlet registration bean")
					.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
		}
		if (containsDispatcherRegistrationBean) {
			// 存在名称为dispatcherServletRegistration的bean,不匹配
			return ConditionOutcome.noMatch(message.found("non servlet registration bean")
					.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
		}
		return ConditionOutcome.match(message.found("servlet registration beans").items(Style.QUOTE, registrations)
				.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
	}

	private ConditionMessage.Builder startMessage() {
		return ConditionMessage.forCondition("DispatcherServlet Registration");
	}

}

WebMvcAutoConfiguration

首先看下WebMvcAutoConfiguration使用了哪些注解

// 当前类是配置类
@Configuration(proxyBeanMethods = false)
// 只有当前应用是servlet应用时才加载
@ConditionalOnWebApplication(type = Type.SERVLET)
// 当类路径中宝苦口Servlet DispatcherServlet WebMvcConfigurer时才进行配置
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
// WebMvcConfigurationSupport不存在时才进行配置
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
// 在DispatcherServletAutoConfiguration,TaskExecutionAutoConfiguration,ValidationAutoConfiguration初始化之后进行初始化
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
}

该类中主要包含如下几个内部类,下面分别进行分析
在这里插入图片描述

WebMvcAutoConfigurationAdapter

// 当前类是配置类
@Configuration(proxyBeanMethods = false)
// 引入EnableWebMvcConfiguration
@Import(EnableWebMvcConfiguration.class)
// 执行WebMvcProperties和ResourceProperties中配置的绑定
// 分别用来解析spring.mvc和spring.resources为前缀的配置项
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
}
viewResolver
@Bean
// 当存在ViewResolver类型的bean才加载
@ConditionalOnBean(ViewResolver.class)
// 当不存在名称为viewResolver且不存在类型为ContentNegotiatingViewResolver的bean
@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;
}

ContentNegotiatingViewResolver虽然继承了ViewResolver,但是并不会直接进行视图的解析,而是通过上下文来找到合适的视图解析器来进行解析

ContentNegotiatingViewResolver

下面的代码会从beanFactory中获取实现了ViewResolver接口的bean放到viewResolvers中

@Override
protected void initServletContext(ServletContext servletContext) {
	// 从spring上下文中找到实现了ViewResolver接口的bean
	Collection<ViewResolver> matchingBeans =
			BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values();
	// 将找到的这些bean添加到viewResovlers中
	if (this.viewResolvers == null) {
		this.viewResolvers = new ArrayList<>(matchingBeans.size());
		for (ViewResolver viewResolver : matchingBeans) {
			if (this != viewResolver) {
				this.viewResolvers.add(viewResolver);
			}
		}
	}
	else {
		for (int i = 0; i < this.viewResolvers.size(); i++) {
			ViewResolver vr = this.viewResolvers.get(i);
			if (matchingBeans.contains(vr)) {
				continue;
			}
			String name = vr.getClass().getName() + i;
			obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(vr, name);
		}

	}
	AnnotationAwareOrderComparator.sort(this.viewResolvers);
	this.cnmFactoryBean.setServletContext(servletContext);
}
public View resolveViewName(String viewName, Locale locale) throws Exception {
	RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
	Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
	List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
	if (requestedMediaTypes != null) {
		// 使用viewResolvers中的解析器来解析当前视图
		List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
		// 选择一个最佳匹配的
		View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
		if (bestView != null) {
			return bestView;
		}
	}

	String mediaTypeInfo = logger.isDebugEnabled() && requestedMediaTypes != null ?
			" given " + requestedMediaTypes.toString() : "";

	if (this.useNotAcceptableStatusCode) {
		if (logger.isDebugEnabled()) {
			logger.debug("Using 406 NOT_ACCEPTABLE" + mediaTypeInfo);
		}
		return NOT_ACCEPTABLE_VIEW;
	}
	else {
		logger.debug("View remains unresolved" + mediaTypeInfo);
		return null;
	}
}

从上面的代码也能看出,ContentNegotiatingViewResolver不会直接解析视图,而是将解析工作交给beanFactory中ViewResolver的实现类bean

BeanNameViewResolver

BeanNameViewResolver根据名称来匹配beanFactory中的View类型的bean

@Bean
// 存在View类型的bean才加载
@ConditionalOnBean(View.class)
// 不存在BeanNameViewResolver类型的bean才加载
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
	BeanNameViewResolver resolver = new BeanNameViewResolver();
	resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
	return resolver;
}
BeanNameViewResolver

BeanNameViewResolver同样继承了ViewResolver,因此同样具有解析视图的能力,但是和上面的ContentNegotiatingViewResolver不同,该类通过视图的名称在beanFactory中寻找同名的类型为View的bean

public class BeanNameViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered {

	private int order = Ordered.LOWEST_PRECEDENCE;  // default: same as non-Ordered


	/**
	 * Specify the order value for this ViewResolver bean.
	 * <p>The default value is {@code Ordered.LOWEST_PRECEDENCE}, meaning non-ordered.
	 * @see org.springframework.core.Ordered#getOrder()
	 */
	public void setOrder(int order) {
		this.order = order;
	}

	@Override
	public int getOrder() {
		return this.order;
	}


	@Override
	@Nullable
	public View resolveViewName(String viewName, Locale locale) throws BeansException {
		ApplicationContext context = obtainApplicationContext();
		// 找到类型为View并且名称匹配视图名称的bean
		if (!context.containsBean(viewName)) {
			// Allow for ViewResolver chaining...
			return null;
		}
		if (!context.isTypeMatch(viewName, View.class)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Found bean named '" + viewName + "' but it does not implement View");
			}
			// Since we're looking into the general ApplicationContext here,
			// let's accept this as a non-match and allow for chaining as well...
			return null;
		}
		return context.getBean(viewName, View.class);
	}

}
静态资源处理

WebMvcAutoConfigurationAdapter实现了WebMvcConfigurer接口,该接口主要是用来作为回调,来对配置进行定制化
对静态资源的处理,主要在addResourceHandlers这个方法中

public void addResourceHandlers(ResourceHandlerRegistry registry) {
	// resourceProperties中的配置项都是spring.resources前缀
	// 是否开启默认的静态资源处理,默认值是true,如果开启,会执行下面的代码来对资源处理进行配置
	if (!this.resourceProperties.isAddMappings()) {
		logger.debug("Default resource handling disabled");
		return;
	}
	Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
	CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
	// 判断是否有对/webjars/**的映射处理,如果没有进行下面的映射配置
	if (!registry.hasMappingForPattern("/webjars/**")) {
		customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
				.addResourceLocations("classpath:/META-INF/resources/webjars/")
				.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
	}
	// 这里的staticPathPattern默认是/**
	// 这里path可以配置
	String staticPathPattern = this.mvcProperties.getStaticPathPattern();
	// 判断是否有对应的映射,默认的映射位置是[/META-INF/resources/, /resources/, /static/, /public/]
	// 因此,默认情况下,我们可以将静态资源放在上述几个目录下面
	if (!registry.hasMappingForPattern(staticPathPattern)) {
		customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
				.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
				.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
	}
}
@EnableWebMvc
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

可以看到@EnableWebMvc注解,主要导入了DelegatingWebMvcConfiguration
下面看下DelegatingWebMvcConfiguration继承层次
在这里插入图片描述

WebMvcConfigurationSupport

首先看下WebMvcConfigurationSupport,实现了ApplicationContextAware和ServletContextAware,所以可以拿到ApplicationContext和ServletContext
另外该类有许多用来注入bean的方法

@Bean
public BeanNameUrlHandlerMapping beanNameHandlerMapping(
		@Qualifier("mvcConversionService") FormattingConversionService conversionService,
		@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

	BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
	mapping.setOrder(2);
	mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
	mapping.setCorsConfigurations(getCorsConfigurations());
	return mapping;
}

并且有很多空的protected修饰符的方法

protected void addViewControllers(ViewControllerRegistry registry) {
}

这些空的protected修饰符的方法会在上面这些注入bean的方法中被调用,这些空的protected修饰符的方法就是提供给子类来进行定制化操作的

DelegatingWebMvcConfiguration

下面看下实现类DelegatingWebMvcConfiguration,从下面的代码可以看到,DelegatingWebMvcConfiguration 首先是一个配置类
其次通过setConfigurers方法注入了WebMvcConfigurer类型的bean
然后实现了WebMvcConfigurationSupport提供的定制化mvc的拓展点方法
在拓展点方法中,会遍历所有注入的WebMvcConfigurer类型的bean,并且调用它们对应的方法来分别执行各自的定制逻辑

@Configuration(proxyBeanMethods = false)
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
	@Nullable
	protected Validator getValidator() {
		return this.configurers.getValidator();
	}

	@Override
	@Nullable
	protected MessageCodesResolver getMessageCodesResolver() {
		return this.configurers.getMessageCodesResolver();
	}

}
定制化mvc配置

如果需要定制化mvc配置,可以通过以下几个方法,其中最简单的并且能够保留spring boot自动配置功能的方式是实现一个WebMvcConfigurer并且注入

implements WebMvcConfigurer

Spring boot自动配置会自动引入DelegatingWebMvcConfiguration,该类会扫描类型为WebMvcConfigurer的bean,并且执行对应的定制化逻辑
通过这种方式实现的定制化, 不会覆盖@EnableAutoConfiguration关于WebMvcAutoConfiguration的配置

@EnableWebMvc + implements WebMvcConfigurer

因为@EnableWebMvc会引入DelegatingWebMvcConfiguration,并且DelegatingWebMvcConfiguration本身继承了WebMvcConfigurationSupport
而Spring boot关于mvc相关的自动化配置的前提是不存在WebMvcConfigurationSupport类型的bean
因此使用这种方式会覆盖@EnableAutoConfiguration关于WebMvcAutoConfiguration的配置

extends WebMvcConfigurationSupport 或者extends DelegatingWebMvcConfiguration

道理同上,都会覆盖@EnableAutoConfiguration关于WebMvcAutoConfiguration的配置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值