官方文档
📖 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、Filterpublic 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:
@Overridepublic 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-patternString 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@Beanpublic 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");}}
