Version: Next

SpringMVC自动配置原理

SpringBoot对SpringMVC如何自动配置SpringMVC,如何扩展和定制SpringMVC

学习途径:

image-20200429133034058

官方文档说自动配置了:

  • 视图解析Bean名称视图解析器
  • 静态资源访问,包括Webjars方式访问静态资源
  • 转换器,格式化器(例如对日期进行格式化)
  • Http消息转换器 (比如转成JSON格式)
  • 消息代码处理器,比如定义错误消息
  • 支持首页映射
  • 支持图标自定义
  • 支持初始化数据绑定
  • 如果想扩展定制MVC配置(拦截器、格式化器、视图控制器),可以添加自己写的类型为WebMvcConfigurer@configuration配置类,但是不加@EnableWebMvc注解

ContentNegotiatingViewResolver 内容协商视图解析器

包含在WebMvcAutoConfiguration

@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使用所有其他视图解析器来定位视图,它应该具有较高的优先级
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}

解析视图代码:

@Nullable // 注解说明:@Nullable 即参数可为null
public View resolveViewName(String viewName, Locale locale) throws Exception {
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
List<MediaType> requestedMediaTypes = this.getMediaTypes(((ServletRequestAttributes)attrs).getRequest());
if (requestedMediaTypes != null) {
// 获取候选的视图对象
List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes);
// 选择一个最适合的视图对象,然后把这个对象返回
View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs);
if (bestView != null) {
return bestView;
}
}
// .....
}

getCandidateViews可以看到对所有视图解析器进行了遍历

提示

ContentNegotiatingViewResolver 这个视图解析器就是用来组合所有的视图解析器的

组合逻辑

有个属性viewResolvers,看看它是在哪里进行赋值的

protected void initServletContext(ServletContext servletContext) {
// 这里它是从beanFactory工具中获取容器中的所有视图解析器
// ViewRescolver.class 把所有的视图解析器来组合的
Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values();
ViewResolver viewResolver;
if (this.viewResolvers == null) {
this.viewResolvers = new ArrayList(matchingBeans.size());
}
// ...
}

自定义一个视图解析器

在主程序中写一个视图解析器

@Bean //放到bean中
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
//我们写一个静态内部类,视图解析器就需要实现ViewResolver接口
private static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String s, Locale locale) throws Exception {
return null;
}
}

转换器和格式化器

WebMvcAutoConfiguration中找到FormattingConversionService

@Bean
@Override
public FormattingConversionService mvcConversionService() {
// 拿到配置文件中的格式化规则
WebConversionService conversionService =
new WebConversionService(this.mvcProperties.getDateFormat());
addFormatters(conversionService);
return conversionService;
}
public String getDateFormat() {
return this.dateFormat;
}
/**
* Date format to use. For instance, `dd/MM/yyyy`. 默认的
*/
private String dateFormat;
提示

可以在配置文件中设置格式

spring:
mvc:
date-format:

SpringMVC配置扩展

新建一个自定义配置类,实现WebMvcConfigurer接口,重写对应的方法就可以实现扩展SpringMVC的功能

//应为类型要求为WebMvcConfigurer,所以我们实现其接口
//可以使用自定义类扩展MVC的功能
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 浏览器发送/test , 就会跳转到test页面;
registry.addViewController("/test").setViewName("test");
}
}
  • 可以重写的方法:

可以看到内容涵盖了拦截器、格式化器、视图控制器等

原理分析

  1. WebMvcAutoConfiguration 是 SpringMVC的自动配置类,里面有一个类WebMvcAutoConfigurationAdapter

  2. 这个类上有一个注解,在做其他自动配置时会导入:@Import(EnableWebMvcConfiguration.class)

  3. 点进EnableWebMvcConfiguration这个类看一下,它继承了一个父类:DelegatingWebMvcConfiguration,其中:

    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);
    }
    }
    }
  4. viewController作为参考,其调用了

    protected void addViewControllers(ViewControllerRegistry registry) {
    this.configurers.addViewControllers(registry);
    }
    public void addViewControllers(ViewControllerRegistry registry) {
    Iterator var2 = this.delegates.iterator();
    while(var2.hasNext()) {
    // 将所有的WebMvcConfigurer相关配置来一起调用!包括我们自己配置的和Spring给我们配置的
    WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
    delegate.addViewControllers(registry);
    }
    }
提示

所有的WebMvcConfiguration都会被作用,不止Spring的配置类,自己的配置类也会被调用;