四、Web场景之静态资源配置原理
前边我们讲过,研究SpringBoot原理的第一步,首先启动SpringBoot,它会帮我们默认加载许多的自动配置类,就是那一堆 xxxAutoConfiguration ,我们想要研究Web场景下的静态资源配置,所以我们直接来到 spring-boot-autoconfigure-2.4.0.jar 包下的web场景 ,里边有一个 WebMvcAutoConfiguration ,大部分SpringMVC功能的自动配置都在这里边,接下来我们就从这里开始研究。
现在,我们拿到了这个类,先不要急着去看内容,首要应该看这个类是否生效,从类上边的这些注解来看,它是生效的
既然是生效的,我们就来看下它给容器中配了什么,因为配了什么,就会有什么样的功能。
来到 WebMvcAutoConfiguration 类往下看,翻着翻着看到下边又有一个类
我们要注意一下这个类,因为这个类上边有一个注解 @EnableConfigurationProperties ,表示配置文件和相关属性进行了绑定,点进去看看是怎么绑定的
得出的结果是:WebMvcProperties == spring.mvc、ResourceProperties == spring.resources
得出这个结论后,我们继续看,咦,发现 WebMvcAutoConfigurationAdapter 这个配置类只有一个有参构造器,我们知道,如果一个配置类只有一个有参构造器,那么这个有参构造器所有参数的值都会从容器中确定 ,所以我们来看下这个构造器的参数,看看它到底获取了哪些东西:
1. 配置类只有一个有参构造器:容器获取参数值
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;
}
我们来分析它的参数及代表的含义:
ResourceProperties :获取和spring.resources绑定的所有的值的对象
WebMvcProperties :获取和spring.mvc绑定的所有的值的对象
ListableBeanFactory :Spring的bean工厂
ObjectProvider :找到所有的HttpMessageConverters
ObjectProvider :找到资源处理器的自定义器
ObjectProvider:DispatcherServlet路径
ObjectProvider<ServletRegistrationBean<?>>:给应用注册Servlet、Filter等等
现在,我们的配置类已经拿到了它想要的所有东西,接下来来看他要干嘛,在这里我们先不去关注其他的一些功能,主要关注资源处理有关的东西,比如我们一直向下找,有一个如下的类
2. 资源处理的默认规则
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//首先拿到了spring.resources下的一个属性addMappings,配置为false就直接返回,下边的代码无效了
//这里表示,我们可以通过配置文件,禁用掉静态资源的路径映射,静态资源就不能访问了
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
//获取缓存策略,这里表示,可以通过配置文件,配置缓存策略
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
//配置webjars的访问规则,也有缓存功能,可以缓存你配置的一段时间
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
//静态资源路径的配置规则,staticPathPattern 在spring.mvc里边
//如果我们配置了,就获取我们配置的值,否则为“/**”,也就是在我们指定的位置去找
//指定默认位置在哪里呢,下边的代码有个resourceProperties.getStaticLocations()
//默认路径的代码贴到这段下边,而且静态资源都有缓存策略
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
默认静态资源路径源码
//下边为四个默认路径
public class ResourceProperties {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
private String[] staticLocations;
private boolean addMappings;
private final ResourceProperties.Chain chain;
private final ResourceProperties.Cache cache;
//下边代码省略没有贴
上边就是资源处理的默认规则的主要代码啦,debug的结果已经在注释写明白了,感兴趣的可以自己debug一下哦!
3. 欢迎页的处理规则
我们继续往下找,发现一个 EnableWebMvcConfiguration 类,里边有一个 welcomePageHandlerMapping() 方法,是用来处理欢迎页的处理规则的,在看这段代码之前,我们先来普及一个知识点, 在SpringMVC中,有一个 HandlerMapping,叫做处理器映射,作用是保存了每一个Handler能处理哪些请求 ,知道了这个以后,我们就可以理解 welcomePageHandlerMapping() 这个方法了,表示谁能处理这个欢迎页的映射规则。我们来看代码
@Bean
//参数已经从容器中获取好了,我们不用考虑
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
//new 并初始化一个WelcomePageHandlerMapping,里边代码在下边展示
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");
}
}
上边就是欢迎页的处理规则主要代码啦,debug的结果已经在注释写明白了,感兴趣的可以自己debug一下哦!
Web场景的静态资源配置原理到这里就分析结束了,下篇再见吧!!!