官方文档
📖 boot-features-spring-mvc-static-content
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.0</version>
</dependency>
静态资源
classpath目录下的:
- META-INF/resources
- public
- resources
- static
以上四个路径全都不拦截,可以直接访问。
访问资源时不需要带上任何路径,直接
请求处理
如果同时有动态接口和静态资源请求路径相同,则优先使用动态接口处理。
不能处理的所有请求又都交给静态资源处理器,静态资源也找不到就会404。
访问前缀
默认无前缀,自定义前缀:
spring:
mvc:
static-path-pattern: /parak/**
访问路径:
改变路径
查看spring.resources.static-locations
源代码:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"};
修改默认的静态资源文件路径:
spring:
resources:
static-locations: [classpath:/parak/]
但是我配置的好像没有什么『卵用』,而且META-INF/resources
下的文件还是可以访问,这个玩意还是不推荐大家使用了。
webjar
官网:https://www.webjars.org
添加相关jar包后后可以自动映射相关的静态资源。
例如,添加jquery相关jar包:
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js
首页支持
只需要在任意静态资源路径下加入一个index.html
即可,但是不可以配置静态资源的访问前缀,否则导致index.html
不能被默认访问。
自定义logo:只需要将一个图片文件命名为favicon.ico
放到静态资源文件目录即可。
配置原理
主要查看:org.springframework.boot.autoconfigure.web.servlet
- DispatcherServletAutoConfiguration: 配置SpringMVC的DispatcherServlet规则
- HttpEncodingAutoConfiguration: 配置Http的编解码
- MultipartAutoConfiguration: 配置文件上传
- ServletWebServerFactoryAutoConfiguration: 配置servlet的web服务器
- WebMvcAutoConfiguration: 配置SpringMVC功能
重点关注WebMvcAutoConfiguration:
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET) // 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
其中有一个内部类WebMvcAutoConfigurationAdapter:
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class,
org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer
@EnableConfigurationProperties
将配置文件与配置类进行绑定,类与配置值的前缀映射如下:
- WebMvcProperties: spring.mvc
- ResourceProperties: spring.resources
- WebProperties: spring.web
配置类只有一个有参构造器,构造器的所有参数都会默认从容器中寻找。
// 有参构造器的参数全部从IOC容器中确定
// resorurceProperties:获取和spring.resource绑定的所有的值的对象
// webMvcProperties:获取和spring.mvc绑定的所有值的对象
// webProperties:获取和spring.web绑定的所有值的对象
// beanFactory:Spring的beanFactory
// messageConvertersProvider:找到所有的HttpMessageConverters
// resourceHandlerRegistrationCustomizerProvider:找到资源处理器的自定义器
// dispatcherServletPath: DisapatchServlet的处理路径
// servletRegistrations:给应用注册Servlet、Filter
public WebMvcAutoConfigurationAdapter(
org.springframework.boot.autoconfigure.web.ResourceProperties resourceProperties,
WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
ObjectProvider<HttpMessageConverters> messageConvertersProvider,
ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
ObjectProvider<DispatcherServletPath> dispatcherServletPath,
ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
this.resourceProperties = resourceProperties.hasBeenCustomized() ? resourceProperties
: webProperties.getResources();
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
this.dispatcherServletPath = dispatcherServletPath;
this.servletRegistrations = servletRegistrations;
this.mvcProperties.checkConfiguration();
}
主要关注资源处理的相关方法addResourceHandlers
:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
// 配置是否启用静态资源映射
// 对应application.properties的spring.web.resources.add-mappings
// 一旦设置为false,后面配置静态文件映射规则的代码逻辑将不会执行
logger.debug("Default resource handling disabled");
return;
}
// 配置静态资源缓存时间
// 对应application.properties的spring.web.resources.cache.period
// 配置后该时间段内资源缓存在浏览器中,状态码为304,不再请求服务器
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
// 配置webjars映射规则
// 全部从classpath:/META-INF/resources/webjars/下面寻找
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)
.setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));
}
// 配置静态路径映射规则,其实就是在URL上添加访问路径前缀
// 对应application.properties的spring.mvc.static-path-pattern
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
// staticLocations默认值就是前面我们提到的CLASSPATH_RESOURCE_LOCATIONS
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl)
.setUseLastModified(this.resourceProperties.getCache().isUseLastModified()));
}
}
再看欢迎页规则的配置:
// 注册bean
@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)) {
logger.info("Adding welcome page template: index");
setRootViewName("index");
}
}