【SpringBoot原理剖析系列】SpringBoot的静态资源配置原理
1 WebMvcAutoConfigurationAdapter静态内部类
- SpringBoot启动之后默认会加载大量的自动配置类,形如 [xxxAutoConfiguration.class];
- 与SpringMVC功能相关的自动配置类为 [WebMvcAutoConfiguration.class];
- 与静态资源配置相关的是其中的 [WebMvcAutoConfigurationAdapter.class] 静态内部类;
- WebMvcAutoConfigurationAdapter类的默认值会从下面三个 [xxxProperties.class] 中去获取:
- WebMvcProperties.class:
@ConfigurationProperties(prefix="spring.mvc") ; - WebProperties.class:
@ConfigurationProperties(prefix="spring.web") ; - ResourceProperties.class:已弃用,由
"spring.web.resources" 来替代; - WebMvcAutoConfigurationAdapter类只有一个有参的构造方法,方法的参数都会从Spring容器中寻找并注入。
@EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class, WebProperties.class})
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
this.resourceProperties = (Resources)(resourceProperties.hasBeenCustomized() ? resourceProperties : webProperties.getResources());
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
this.dispatcherServletPath = dispatcherServletPath;
this.servletRegistrations = servletRegistrations
}
}
2 静态资源访问与放置
- 我们可以选择是否开启默认的静态资源处理功能,通过
"spring.web.resources.add-mappings" 在核心配置文件中进行配置,默认为true; - SpringBoot会帮助我们自动添加有关 [webjars] 的配置规则,例如:http://localhost:8888/webjars/jquery/3.5.1/jquery.js,但是现在用的很少;
- 核心的静态资源处理策略:
- 静态资源的访问前缀:对静态资源的访问必须遵循 [当前项目根目录(+/+spring.mvc.static-path-pattern)+/+静态资源名称] 的书写规则,
"spring.mvc.static-path-pattern" 的默认值为"/**"; - 静态资源的放置目录:静态资源要存放于类路径下(/resources)指定的目录中,可以通过
"spring.web.resources.static-locations" 来指定,默认存放在 [/static|/public|/resources|/META-INF/resources] 中。
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{resource});
}
});
}
}
3 欢迎页配置
- 欢迎页配置的相关内容在 [WebMvcAutoConfiguration.class] 自动配置类中的 [EnableWebMvcConfiguration.class] 静态内部类中,最终调用 [WelcomePageHandlerMapping.class] 的构造方法;
- 只需把
"index.html" 放置在静态资源目录下即可生效,但是此时不能手动配置静态资源的访问前缀; - 同理,网页图标的静态资源图片也只需要放置于静态资源目录下即可生效,但名称必须是 [favicon.ico];
- 如果没有放置欢迎页,则会去寻找相应的Controller来处理(请求映射为"/index")。
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({WebProperties.class})
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());
return welcomePageHandlerMapping;
}
}
final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping {
private static final Log logger = LogFactory.getLog(WelcomePageHandlerMapping.class);
private static final List<MediaType> MEDIA_TYPES_ALL;
WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) {
if (welcomePage != null && "/**".equals(staticPathPattern)) {
logger.info("Adding welcome page: " + welcomePage);
this.setRootViewName("forward:index.html");
} else if (this.welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
logger.info("Adding welcome page template: index");
this.setRootViewName("index");
}
}
}
|