Version: Next
SpringMVC自动配置原理
SpringBoot对SpringMVC如何自动配置SpringMVC,如何扩展和定制SpringMVC
学习途径:
- 源码分析
- 官方文档
官方文档说自动配置了:
- 视图解析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");
}
}
- 可以重写的方法:
可以看到内容涵盖了拦截器、格式化器、视图控制器等
原理分析
WebMvcAutoConfiguration
是 SpringMVC的自动配置类,里面有一个类WebMvcAutoConfigurationAdapter
这个类上有一个注解,在做其他自动配置时会导入:
@Import(EnableWebMvcConfiguration.class)
点进
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);}}}以
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的配置类,自己的配置类也会被调用;