一 点睛
Spring Boot提供的Web自动化配置在WebMvcAutoConfiguration和WebMvcProperties的源码中,它的位置如下:
下面来解读这一块的源码。
二 WebMvcAutoConfiguration源码解读
package org.springframework.boot.autoconfigure.web;
......
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter(DispatcherServletAutoConfiguration.class)
public class WebMvcAutoConfiguration {
public static String DEFAULT_PREFIX = "";
public static String DEFAULT_SUFFIX = "";
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
@Bean
@ConditionalOnMissingBean(HttpPutFormContentFilter.class)
public OrderedHttpPutFormContentFilter httpPutFormContentFilter() {
return new OrderedHttpPutFormContentFilter();
}
// Defined as a nested config to ensure WebMvcConfigurerAdapter is not read when not
// on the classpath
@Configuration
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter {
private static final Log logger = LogFactory
.getLog(WebMvcConfigurerAdapter.class);
@Autowired
private ResourceProperties resourceProperties = new ResourceProperties();
@Autowired
private WebMvcProperties mvcProperties = new WebMvcProperties();
@Autowired
private ListableBeanFactory beanFactory;
//声明了了messageConverters
@Autowired
private HttpMessageConverters messageConverters;
@Autowired(required = false)
ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
//注入messageConverters
//此处注入了ByteArrayHttpMessageConverter、StringHttpMessageConverter、ResourceHttpMessageConverter
//SourceHttpMessageConverter、AllEncompassingFormHttpMessageConverter
//在HttpMessageConvertersAutoConfiguration类中,还引入了GsonAutoConfiguration和JacksonAutoConfiguration
//所有我们得到的HttpMessageConverter还是比较丰富的。
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.addAll(this.messageConverters.getConverters());
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
Long timeout = this.mvcProperties.getAsync().getRequestTimeout();
if (timeout != null) {
configurer.setDefaultTimeout(timeout);
}
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Map<String, MediaType> mediaTypes = this.mvcProperties.getMediaTypes();
for (Entry<String, MediaType> mediaType : mediaTypes.entrySet()) {
configurer.mediaType(mediaType.getKey(), mediaType.getValue());
}
}
//通过设置前缀、后缀,以及控制器中方法来返回视图名字符串
//以得到实际页面
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
@Bean
@ConditionalOnMissingBean({ RequestContextListener.class,
RequestContextFilter.class })
public RequestContextFilter requestContextFilter() {
return new OrderedRequestContextFilter();
}
//在控制器中的一个方法返回值的字符串(视图名)会根据BeanNameViewResolver去查找Bean的名称为返回字符串
//的View来渲染视图
@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不是自己处理View,而是代理不同的ViewResolver来处理不同的View
// 它有最高的优先级
resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);
return resolver;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
@Bean
@ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")
public Formatter<Date> dateFormatter() {
return new DateFormatter(this.mvcProperties.getDateFormat());
}
@Override
public MessageCodesResolver getMessageCodesResolver() {
if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
resolver.setMessageCodeFormatter(
this.mvcProperties.getMessageCodesResolverFormat());
return resolver;
}
return null;
}
//只要定义了Converter、GenericConverter和Formatter接口的实现类,这些Bean
//就会自动注册到Spring MVC中
@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();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Integer cachePeriod = this.resourceProperties.getCachePeriod();
//webjars就是将我们的脚本框架封装在jar包中的jar包
//webjars的内容可访问https://www.webjars.org/
//把webjar的/META-INF/resources/webjars/下的静态文件映射为/webjars/**,
//可以通过http://localhost:8080/webjar/**来访问
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(
registry.addResourceHandler("/webjars/**")
.addResourceLocations(
"classpath:/META-INF/resources/webjars/")
.setCachePeriod(cachePeriod));
}
//staticPathPattern为/**,
//资源路径为:"classpath:/META-INF/resources/", "classpath:/resources/","classpath:/static/", "classpath:/public/"
//把类路径下的/static、/public、/resources和/META-INF/resources文件夹下的静态文件直接映射为/**
//可以通过http://localhost:8080/**来访问
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(
registry.addResourceHandler(staticPathPattern)
.addResourceLocations(
this.resourceProperties.getStaticLocations())
.setCachePeriod(cachePeriod));
}
}
private void customizeResourceHandlerRegistration(
ResourceHandlerRegistration registration) {
if (this.resourceHandlerRegistrationCustomizer != null) {
this.resourceHandlerRegistrationCustomizer.customize(registration);
}
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//静态首页:/META-INF/resources/index.html, /resources/index.html,/static/index.html, /public/index.html
//当我们访问应用根目录http://localhost:8080,会直接映射
Resource page = this.resourceProperties.getWelcomePage();
if (page != null) {
logger.info("Adding welcome page: " + page);
registry.addViewController("/").setViewName("forward:index.html");
}
}
......
}