1、SpringMVC功能的自动配置类 WebMvcAutoConfiguration 类。
- SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)。
- SpringMVC功能的自动配置类 WebMvcAutoConfiguration,生效。
1、@Configuration``(proxyBeanMethods = ``false``)
这是一个多实例的配置类。
2、@ConditionalOnWebApplication``(type = Type.``_SERVLET_``)
Servlet web应用。
3、@ConditionalOnClass``({ Servlet.``class``, DispatcherServlet.``class``, WebMvcConfigurer.``class ``})
三个组件类。
4、@ConditionalOnMissingBean``(WebMvcConfigurationSupport.``class``)
容器中没有 WebMvcConfigurationSupport.``class
这个组件时,WebMvcAutoConfiguration
类中的所有配置才生效,反之所有配置不生效。
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
2、SpringMVC功能的自动配置类 **WebMvcAutoConfiguration**
类,给容器配置了什么?
**WebMvcAutoConfiguration**
类中的内部类 WebMvcAutoConfigurationAdapte``r
。
1、@Configuration``(proxyBeanMethods = ``false``)
这是一个配置类多实例配置类。
2、@Import``(EnableWebMvcConfiguration.``class``)
导入 EnableWebMvcConfiguration 组件
3、@EnableConfigurationProperties
({ WebMvcProperties.class, ResourceProperties.class }) 配置类与 WebMvcProperties``.``class
、ResourceProperties``.``class
绑定。
1)、WebMvcProperties``.``class
由源码得出,与 spring.mvc 绑定。
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
2)、ResourceProperties.``class
由源码得出,与 spring.resources 绑定。
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties {
4、禁用所有静态资源规则(true开启禁用,false关闭禁用)
spring:
# mvc:
# static-path-pattern: /res/**
#
web:
resources:
static-locations: [classpath:/haha/]
#设置开启或禁用静态资源规则
add-mappings: true
5、自定义静态资源缓存时长(单位秒)。
spring:
# mvc:
# static-path-pattern: /res/**
#
web:
resources:
static-locations: [classpath:/haha/]
add-mappings: true
#自定义缓存时长
cache:
period: 1
**WebMvcAutoConfiguration**
类中的内部类 WebMvcAutoConfigurationAdapte``r
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);
private final ResourceProperties resourceProperties;
private final WebMvcProperties mvcProperties;
private final ListableBeanFactory beanFactory;
private final ObjectProvider<HttpMessageConverters> messageConvertersProvider;
private final ObjectProvider<DispatcherServletPath> dispatcherServletPath;
private final ObjectProvider<ServletRegistrationBean<?>> servletRegistrations;
final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
//只有一个有参构造器时,所有参数的值都会从容器中确定。
//ResourceProperties resourceProperties:获取@EnableConfigurationProperties注解与 spring.resources 绑定的所有的值的对象。
//WebMvcProperties mvcProperties:获取@EnableConfigurationProperties注解 与 spring.mvc 绑定的所有的值的对象。
//ListableBeanFactory beanFactory:Spring的bean工厂beanFactory。
//ObjectProvider<HttpMessageConverters> messageConvertersProvider 找到所有messageConvertersProvider。
//ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider 找到资源处理器的自定义器。
//ObjectProvider<DispatcherServletPath> dispatcherServletPath 资源处理的路径。
//ObjectProvider<ServletRegistrationBean<?>> servletRegistrations 给应用注册Servlet、Filter等等。。。
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,
ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
ObjectProvider<DispatcherServletPath> dispatcherServletPath,
ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
this.resourceProperties = resourceProperties;
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
this.dispatcherServletPath = dispatcherServletPath;
this.servletRegistrations = servletRegistrations;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
this.messageConvertersProvider
.ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
}
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
if (this.beanFactory.containsBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)) {
Object taskExecutor = this.beanFactory
.getBean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME);
if (taskExecutor instanceof AsyncTaskExecutor) {
configurer.setTaskExecutor(((AsyncTaskExecutor) taskExecutor));
}
}
Duration timeout = this.mvcProperties.getAsync().getRequestTimeout();
if (timeout != null) {
configurer.setDefaultTimeout(timeout.toMillis());
}
}
@Override
@SuppressWarnings("deprecation")
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(this.mvcProperties.getPathmatch().isUseSuffixPattern());
configurer.setUseRegisteredSuffixPatternMatch(
this.mvcProperties.getPathmatch().isUseRegisteredSuffixPattern());
this.dispatcherServletPath.ifAvailable((dispatcherPath) -> {
String servletUrlMapping = dispatcherPath.getServletUrlMapping();
if (servletUrlMapping.equals("/") && singleDispatcherServlet()) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setAlwaysUseFullPath(true);
configurer.setUrlPathHelper(urlPathHelper);
}
});
}
private boolean singleDispatcherServlet() {
return this.servletRegistrations.stream().map(ServletRegistrationBean::getServlet)
.filter(DispatcherServlet.class::isInstance).count() == 1;
}
@Override
@SuppressWarnings("deprecation")
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
WebMvcProperties.Contentnegotiation contentnegotiation = this.mvcProperties.getContentnegotiation();
configurer.favorPathExtension(contentnegotiation.isFavorPathExtension());
configurer.favorParameter(contentnegotiation.isFavorParameter());
if (contentnegotiation.getParameterName() != null) {
configurer.parameterName(contentnegotiation.getParameterName());
}
Map<String, MediaType> mediaTypes = this.mvcProperties.getContentnegotiation().getMediaTypes();
mediaTypes.forEach(configurer::mediaType);
}
@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;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
@Override
public MessageCodesResolver getMessageCodesResolver() {
if (this.mvcProperties.getMessageCodesResolverFormat() != null) {
DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver();
resolver.setMessageCodeFormatter(this.mvcProperties.getMessageCodesResolverFormat());
return resolver;
}
return null;
}
@Override
public void addFormatters(FormatterRegistry registry) {
ApplicationConversionService.addBeans(registry, this.beanFactory);
}
//资源处理的默认规则
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//ResourceProperties与配置文件spring.resources绑定。
//isAddMappings()的值,决定资源是否被禁用,true不禁用,false禁用。
if (!this.resourceProperties.isAddMappings()) { //判断isAddMappings静态资源是否被禁用,
logger.debug("Default resource handling disabled"); //判断结果为false提示:静态资源被禁用。
return;
}
//getPeriod()静态资源缓存,保存多长时间,可在配置文件中自定义时长。单位:秒。
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
//获取缓存。
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
//hasMappingForPattern("/webjars/**") 判断判断路径是否存在的方法,是ResourceHandlerRegistry与spring.resources配置文件绑定的类中定义的方法。
//registry.hasMappingForPattern("/webjars/**") 获取spring.resources配置文件中是否存在路径"/webjars/**" 路径。
if (!registry.hasMappingForPattern("/webjars/**")) {//"/webjars/**"判断给registry里注册访问规则"/webjars/**" 。
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")//访问"/webjars/**"下的所有请求时,都会注册/webjars/** 下的所有请求。
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)); //访问/webjars/** 下的请求,都会去"classpath:/META-INF/resources/webjars/"路径下找。
}
//通过mvcProperties获取与getStaticPathPattern()绑定的 spring.mvc 配置文件信息中的配置路径 static-path-pattern: /res/** 。
//没有使用默认路径访问规则 /**。
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {//获取路径,并给registry注册路径规则。
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)//注册访问路径下的,被访问的请求。
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))//getStaticLocations()下的默认值: "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/"
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));//staticPathPattern 下的请求,都会去"classpath:/META-INF/resources/webjars/"路径下找。
}
}
private Integer getSeconds(Duration cachePeriod) {
return (cachePeriod != null) ? (int) cachePeriod.getSeconds() : null;
}
private void customizeResourceHandlerRegistration(ResourceHandlerRegistration registration) {
if (this.resourceHandlerRegistrationCustomizer != null) {
this.resourceHandlerRegistrationCustomizer.customize(registration);
}
}
@Bean
@ConditionalOnMissingBean({ RequestContextListener.class, RequestContextFilter.class })
@ConditionalOnMissingFilterBean(RequestContextFilter.class)
public static RequestContextFilter requestContextFilter() {
return new OrderedRequestContextFilter();
}
}
欢迎页的处理规则
HandlerMapping:处理器映射。保存了每一个Handler能处理哪些请求。
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
return welcomePageHandlerMapping;
}
WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
ApplicationContext applicationContext, Optional<Resource> welcomePage, String staticPathPattern) {
if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {
//要用欢迎页功能,必须是/**
logger.info("Adding welcome page: " + welcomePage.get());
setRootViewName("forward:index.html");
}
else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
// 调用Controller /index
logger.info("Adding welcome page template: index");
setRootViewName("index");
}
}