SpringBoot 之 SpringMvc自动配置

SpringMVC自动配置

详情请参考官网:https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/reference/htmlsingle/#boot-features-developing-web-applications

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 and BeanNameViewResolver beans.
  • Support for serving static resources, including support for WebJars (covered later in this document)).
  • Automatic registration of Converter, GenericConverter, and Formatter 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:

  1. 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容器就好了
  1. Support for serving static resources, including support for WebJars (covered later in this document)).
    提供了静态资源的支持包括WebJars,可以去参考另一篇文章:Springboot对静态资源的映射规则

  2. 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();
    }
  1. Support for HttpMessageConverters (covered later in this document).

支持HttpMessageConverters HTTP消息转换器,如果方法里面返回一个User对象,就需要有一个转换器来将对象序列化成JSON
HttpMessageConverters也是一个配置类,详情可以去看看源代码
HttpMessageConverters也是从IOC容器中确定使用哪个HttpMessageConverters.
也可以添加自己的HttpMessageConverters,只需要添加进IOC容器就好
可以看看官方的demo,我也截图了官网的demo


HttpMessageConverter.png
  1. 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. 静态首页访问

  1. 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) {

    }

    还有很多方法..
    
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容