十六、Spring Boot自动配置SpringMVC

1、SpringMVC自动配置官方文档

Spring Boot官方文档:Spring Boot中Springmvc配置文档

2、Spring MVC auto-configuration

Spring Boot 提供了大多数SpringMVC应用常用的自动配置项。

以下是Spring Boot对SpringMVC的默认配置(来自官网,自行翻译):

  • 自动配置了 ContentNegotiatingViewResolverBeanNameViewResolver 的Beans.
    • 给容器自定义添加一个视图解析器,该ContentNegotiatingViewResolver 的bean会自动组合进来。
    • 自动配置了ViewResolver:ContentNegotiatingViewResolver是组合了所有的视图解析器
public class WebMvcAutoConfiguration {
    //other code...
    
    @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;
    }
}

其中ContentNegotiatintViewResolver类实现ViewResoler接口:

public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
        implements ViewResolver, Ordered, InitializingBean {
    //other code...
    
    @Override
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
        Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
        //获取所有的MediaType,例如application/json,text/html等等。。。
        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;
            }
        }
        //other code...
    }
    
    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);
                    }
                }
            }
        }
        //other code...
        
        return candidateViews;
    }
}
  • 支持静态资源文件夹和webjars
  • 静态首页的访问。
  • 自定义favicon图标
  • 自动注册了 Converter, GenericConverter, Formatter Beans.
    • Converter:转换器,用于类型转换
    • Formatter:格式化器
@Bean
//如果配置了spring.mvc.date-format,则自动注册Formatter<Date>的Bean
@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
public Formatter<Date> dateFormatter() {
    return new DateFormatter(this.mvcProperties.getDateFormat());
}

自动添加的格式化转换器,只需添加到容器中即可。

  • 支持HttpMessageConverters 消息转换器。

    • HttpMessageConverter:SpringMVC用来转换Http请求和响应的
    • HttpMessageConverters` 是从容器中获取的所有的HttpMessageConverter如果需要给容器中添加HttpMessageConverter,只需要将自定义的组件注册在容器中即可。
  • 自动配置 MessageCodesResolver 用于错误代码的生成规则。

    • PREFIX_ERROR_CODE: error_code + "." + object name + "." + field
    • POSTFIX_ERROR_CODE:object name + "." + field + "." + error_code
  • 自动使用 ConfigurableWebBindingInitializerBean。

    • 可以自定义配置一个ConfigurableWebBindingInitializer来替换默认的,需要添加到容器中。
    • 初始化WebDataBinder:用于将请求数据绑定到数据模型等。

包org.springframework.boot.autoconfigure.web是web的所有自动配置场景。

​ 来自官网:

​ If you want to keep Spring Boot MVC features, and you just want to add additional MVC configuration (interceptors, formatters, view controllers etc.) you can add your own @Configuration class of type WebMvcConfigurerAdapter, but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter or ExceptionHandlerExceptionResolver you can declare a WebMvcRegistrationsAdapter instance providing such components.

​ If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

3、扩展SpringMVC

编写一个配置类,是WebMvcConfigurerAdapter类的子类,但是不能标注@EnableWebMvc注解。

这样既保留了所有的自动配置,也能使用自定义的扩展配置。

//使用WebMvcConfigurerAdapter可以来扩展SpringMVC的功能
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter{

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
//      super.addViewControllers(registry);
        //浏览器发送/cay请求,会直接跳到success页面。
        registry.addViewController("/cay").setViewName("success");
    }
}

原理:

  • 1)、WebMvcAutoConfiguration是SpringMVC的自动配置类,在内部维护了一个内部类WebMvcAutoConfigurationAdapter,该类又继承自WebMvcConfigurerAdapter,看定义:
@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {}

​ 而WebMvcConfigurerAdapter又实现了WebMvcConfigurer接口:

public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {}

​ 所以自定义的配置类(此例为MyMvcConfig)是个WebMvcConfigurer接口的实现类。

  • 2)、在做WebMvcAutoConfigurationAdapter自动配置时会导入@Import(EnableWebMvcConfiguration.class)
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {}

父类:

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

    //从容器中获取所有的WebMvcConfigurer
    @Autowired(required = false)
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }
    }
}
  • 3)、容器中所有的WebMvcConfigurer都会一起起作用。
class WebMvcConfigurerComposite implements WebMvcConfigurer {

    private final List<WebMvcConfigurer> delegates = new ArrayList<WebMvcConfigurer>();

    public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.delegates.addAll(configurers);
        }
    }
    
    @Override
    public void addFormatters(FormatterRegistry registry) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.addFormatters(registry);
        }
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.addInterceptors(registry);
        }
    }
    
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        for (WebMvcConfigurer delegate : this.delegates) {
            delegate.addViewControllers(registry);
        }
    }
    
    //other code...
}

从源码中可以看到,从容器中获取的所有WebMvcConfigurer对象都会被调用对应的配置方法。

  • 4)、最后可以结合第2点和第3点看出,自定义的配置类也会被调用。

总结:SpringMVC的自动配置和自定义的扩展配置都会起作用。


4、全面接管SpringMVC

​ 在配置类上使用@EnableWebMvc注解,这样Spring Boot对SpringMVC的自动配置就失效了,所有都需要自定义配置。

原理:为什么使用了@EnableWebMvc后自动配置就失效了?

  • 1)、EnableWebMvc注解的定义
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {}

  • 2)、导入了DelegatingWebMvcConfiguration组件配置
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {}

  • 3)、查看WebMvcAutoConfiguration自动配置类的签名
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
        WebMvcConfigurerAdapter.class })
//如果容器中没有该组件的时候,这个自动配置类就自动生效。
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {}

  • 4)、@EnableWebMvc将WebMvcConfigurationSupport组件导入进来了,导致Spring Boot对SpringMVC的自动配置失效,即WebMvcAutoonfiguration配置类未注册成功。

5、修改SpringBoot的默认配置

模式:

  • 1)、Spring Boot在自动配置很多组件的时候,会先检查容器中是否有用户自定义配置的Bean或者组件。如果有,就使用用户自定义的;如果没有,Spring Boot才自动配置;如果有些组件可以有多个,用户可以自定义添加组件,并加入到容器中,这样Spring Boot会自动将用户配置的和默认的组合起来。

  • 2)、在Spring Boot中会有很多的Configurer帮助用户进行扩展配置。

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

推荐阅读更多精彩内容