SpringMVC自动配置
springboot引入starter-web之后,开发web项目简直就是撸起袖子就干,那么对底层的装配究竟是怎么样的呢?
以下有一段官网的解释:
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
The auto-configuration adds the following features on top of Spring’s defaults:
- Inclusion of
ContentNegotiatingViewResolver
andBeanNameViewResolver
beans. - Support for serving static resources, including support for WebJars (covered later in this document)).
- Automatic registration of
Converter
,GenericConverter
, andFormatter
beans. - Support for
HttpMessageConverters
(covered later in this document). - Automatic registration of
MessageCodesResolver
(covered later in this document). - Static
index.html
support. - Custom
Favicon
support (covered later in this document). - Automatic use of a
ConfigurableWebBindingInitializer
bean (covered later in this document).
下面对官网的文档做出分析
springboot提供了springmvc工作时大多数场景自动配置,可以理解为自动配置好了springmvc
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
默认配置了那些功能?
The auto-configuration adds the following features on top of Spring’s defaults:
- Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
包含 ContentNegotiatingViewResolver 和 BeanNameViewResolver 组件(beans)
ContentNegotiatingViewResolver 从后缀(ViewResolver)可以看出是一个视图解析器
ContentNegotiatingViewResolver 这个组件的作用是,根据方法返回的值得到视图对象(View)
然后视图对象决定如何去工作,比如重定向还是什么的.
可以看在WebMvcAutoConfiguration类,看看源代码是怎么写的
@Bean //注册一个组件
/*@ConditionalOnBean是 IOC容器中如果存在ViewResolver 这个组件才会注册成功,就是加了一个条件判断*/
@ConditionalOnBean(ViewResolver.class)
/*IOC容器中不存在ContentNegotiatingViewResolver条件才满足*/
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {
/*
在ContentNegotiatingViewResolver类中有一个resolveViewName方法会返回一个最优的View对象
此类中还有getBestView方法,可以自己去debug看看.
也有initServletContext方法,里面有获取所有视图解析器的代码,详细信息请看源代码
*/
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(
beanFactory.getBean(ContentNegotiationManager.class));
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}
可以给容器添加一个自定义的解析器
实现一个ViewResolver接口,然后将实现类注册进IOC容器就好了
Support for serving static resources, including support for WebJars (covered later in this document)).
提供了静态资源的支持包括WebJars,可以去参考另一篇文章:Springboot对静态资源的映射规则Automatic registration of Converter, GenericConverter, and Formatter beans.
自动注册了 Converter(转换器),GenericConverter,Formatter组件(bean)
转换器的介绍可以去看看springmvc官方
Converter : springmvc 类型转换是使用了Converter,客户端传一个true的话,是需要Converter转换为布尔类型的1
Formatter : 客户端传一个2018-05-15的字符串过来,需要Formatter来格式化成一个Date类
WebMvcAutoConfiguration类里面有一个方法addFormatters,可以让开发者添加自己的格式化.
@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();
}
- Support for HttpMessageConverters (covered later in this document).
支持HttpMessageConverters HTTP消息转换器,如果方法里面返回一个User对象,就需要有一个转换器来将对象序列化成JSON
HttpMessageConverters也是一个配置类,详情可以去看看源代码
HttpMessageConverters也是从IOC容器中确定使用哪个HttpMessageConverters.
也可以添加自己的HttpMessageConverters,只需要添加进IOC容器就好
可以看看官方的demo,我也截图了官网的demo
- Automatic registration of MessageCodesResolver
自动注册MessageCodesResolver ,就是支持MessageCodesResolver.
在WebMvcAutoConfiguration配置类中有一个getMessageCodesResolver方法
@Override
public MessageCodesResolver getMessageCodesResolver() {
if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
resolver.setMessageCodeFormatter(
this.mvcProperties.getMessageCodesResolverFormat());
return resolver;
}
return null;
}
this.mvcProperties.getMessageCodesResolverFormat() 返回messageCodesResolverFormat
messageCodesResolverFormat又是DefaultMessageCodesResolver.Format
Format类是一个错误代码规则,以下是源代码
public static enum Format implements MessageCodeFormatter {
PREFIX_ERROR_CODE {
public String format(String errorCode, @Nullable String objectName, @Nullable String field) {
return toDelimitedString(new String[]{errorCode, objectName, field});
}
},
POSTFIX_ERROR_CODE {
public String format(String errorCode, @Nullable String objectName, @Nullable String field) {
return toDelimitedString(new String[]{objectName, field, errorCode});
}
};
6 .Static index.html support. 静态首页访问
- Automatic use of a ConfigurableWebBindingInitializer bean
注册使用ConfigurableWebBindingInitializer 组件
ConfigurableWebBindingInitializer是什么东西? 可以从命名中猜一下
WebBindingInitializer 是 绑定请求数据
客户端传一个 url?username=Elson&age=27 可以绑定到对应的User类中.
也就是springmvc的数据双向绑定
可以自定义配置ConfigurableWebBindingInitializer来替换SprintBoot默认的
在WebMvcAutoConfiguration配置类中有一个getConfigurableWebBindingInitializer方法
@Override
protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer() {
try {
bean工厂里面没有ConfigurableWebBindingInitializer,就调用父类的getConfigurableWebBindingInitializer()
return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class);
}
catch (NoSuchBeanDefinitionException ex) {
return super.getConfigurableWebBindingInitializer();
}
}
ConfigurableWebBindingInitializer类中有initBinder方法
可以进一步的了解,springmvc是怎么做到数据绑定的
public void initBinder(WebDataBinder binder) {
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) {
PropertyEditorRegistrar[] var2 = this.propertyEditorRegistrars;
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
PropertyEditorRegistrar propertyEditorRegistrar = var2[var4];
propertyEditorRegistrar.registerCustomEditors(binder);
}
}
}
Note: org.springframework.boot.autoconfigure.web web的所有自动配置场景都在里面
如何修改SprintBoot的默认配置
开发者优先模式
SpringBoot在自动配置很多组件的时候,先查看IOC容器中,是否有开发者自己配置的
以WebMvcAutoConfiguration类来说
@Bean
如果容器中没有HiddenHttpMethodFilter,才注册OrderedHiddenHttpMethodFilter
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
换句话来说,我们自己可以定制一个HiddenHttpMethodFilter组件,注册到IOC容器就好
混合模式
开发者也可以定制配置注册到容器里面,然后也会使用默认已经配置好的组件,组合起来一起使用,比如说多个视图解析器(ViewResolver)
单靠springboot默认自动配置的组件,是不够用的,有时候需要添加项目所需要的配置
官方给出一段话:
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration
class of type WebMvcConfigurer
but without @EnableWebMvc
. If you wish to provide custom instances of RequestMappingHandlerMapping
, RequestMappingHandlerAdapter
, or ExceptionHandlerExceptionResolver
, you can declare a WebMvcRegistrationsAdapter
instance to provide such components.
If you want to take complete control of Spring MVC, you can add your own @Configuration
annotated with @EnableWebMvc
.
如果想保持springboot mvc的功能,又想添加额外的mvc 配置 比如interceptors(拦截器),formatters(格式化),view controllers(视图控制),和其他功能
你可以添加你自己的 WebMvcConfigurer类型的 @Configuration配置类,不能标注@EnableWebMvc注解
就是实现WebMvcConfigurer接口,然后标注一个@Configuration注解,然后不能标注@EnableWebMvc
如果你希望提供,RequestMappingHandlerMapping,RequestMappingHandlerAdapter或者ExceptionHandlerExceptionResolver 自定义实例
可以声明WebMvcRegistrationsAdapter实例来提供这些组件
大体都这样翻译一下吧,下面给出事例代码
如果觉得方法太多的话,可以自己写一个适配器模式,之前的版本也提供了 WebMvcConfigurerAdapter 适配类,但是过时了.
这不就等于以前的spirngmvc配置文件吗..也可以这么理解
说明一下WebMvcConfigurer接口的方法
Note: 如果加上@EnableWebMvc那么,SpringBoot对Springmvc的所有自动配置都失效,有兴趣也分析一下@EnableWebMvc
@Configuration
public CustomSpringMvcConfiguration implements WebMvcConfigurer {
/* 视图控制 */
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//youaresohandsome请求会映射到yesIdo界面
registry.addViewController("/youaresohandsome").setViewName("yesIdo");
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
}
/* 添加跨域映射 */
@Override
public void addCorsMappings(CorsRegistry registry) {
}
/* 配置视图解析器 */
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
}
还有很多方法..
}