1. 主要类:
WebMvcAutoConfiguration: MVC的自动配置类
EnableWebMvcConfiguration: 启用WebMvcConfiguration的类
DelegatingWebMvcConfiguration: WebMvcConfiguration委托的代理类
WebMvcConfigurerComposite: “具体WebMvcConfiguration的委托类”
`
WebMvcAutoConfigurationAdapter: MVC自动配置适配器,其实现了WebMvcConfiguration
WebMvcConfigurer: MVC配置接口
WebMvcConfigurerAdapter: MVC配置适配器(1.8后弃用,因为接口支持default默认实现)
2. 视图解析器初始化:
springboot启动后会自动配置mvc的组件,即WebMvcAutoConfiguration,自动进行mvc的一些配置
其内部静态类WebMvcAutoConfigurationAdapter也是一个组件,通过@Import导入EnableWebMvcConfiguration组件
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
EnableWebMvcConfiguration继承了DelegatingWebMvcConfiguration委托代理类
@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
代理类通过setConfigurers方法来初始化mvc配置的具体委托类
@Autowired会从容器总把实现WebMvcConfigurer接口的类都注入到configurers
然后将所有的mvc配置类添加到WebMvcConfigurerComposite
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
//也实现了WebMvcConfigurer当并没有@Configuration到容器中
class WebMvcConfigurerComposite implements WebMvcConfigurer {
private final List<WebMvcConfigurer> delegates = new ArrayList<>();
public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.delegates.addAll(configurers);
}
}
dubug可以知道,默认使用WebMvcAutoConfigurationAdapter
所以,要想自定义mvc的配置,只需要实现WebMvcConfigurer接口,并将该类放入容器中即可
@Configuration
public class MyConfigure implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/testView").setViewName("success");
}
}
3. MVC视图功能配置:
由上可知,mvc的配置,默认使用WebMvcAutoConfigurationAdapter来配置
这个类在配置的时候,会往容器中添加三个视图bean,主要关注ContentNegotiatingViewResolver注意其该配置resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);设置最高级的Order,后续会提到
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
... ... ...
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
@Bean
@ConditionalOnBean(View.class)
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {
BeanNameViewResolver resolver = new BeanNameViewResolver();
resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return resolver;
}
@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;
}
... ... ...
ContentNegotiatingViewResolver这个视图解析器主要是用来组合所有视图解析器的
在其内部实现方法initServletContext可以看到,将所有视图解析器(除本类)添加到本类的中viewResolvers
可见,想要自定义视图解析器,可以实现ViewResolver接口,然后加入容器即可,本文不论述
4. MVC解析视图过程:
前端控制器(DispatcherServlet),在第一次调用视图解析器之前,会通过initViewResolvers方法先初始化视图解析器
该方法中,获取容器内所有视图解析器,即容器中实现ViewResolver接口的类
然后所有视图解析器根据Order排序,上文提及ContentNegotiatingViewResolver设置了最高级别Order,所以它会排在第一个
当初始化视图解析器后,前端控制器通过resolveViewName方法遍历所有视图解析器来解析,能解析立即返回View
ContentNegotiatingViewResolver排在第一个,所以会最先调用
其内部方法通过getCandidateViews来解析出所有视图,最后处理返回一个bestView给前端控制器
在getCandidateViews方法中,this.viewResolvers就是之前ContentNegotiatingViewResolver组合的所有视图解析器
调用各视图解析器的实现方法resolveViewName来解析视图
有兴趣的可以研究一些这个方法,我就算了
详细的MVC执行过程可参考:SpringBoot(2)-MVC执行过程