上一篇博文说明了SpringBoot对WebJars的支持,那么项目中还有其他静态资源文件,如自定义CSS、JS、image等,SpringBoot同样提供了映射支持。
【1】静态资源文件映射规则
同样查看WebMvcAutoConfiguration源码如下:
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } //对webjars做映射处理 Integer cachePeriod = this.resourceProperties.getCachePeriod(); if (!registry.hasMappingForPattern("/webjars/**")) { customizeResourceHandlerRegistration( registry.addResourceHandler("/webjars/**") .addResourceLocations( "classpath:/META-INF/resources/webjars/") .setCachePeriod(cachePeriod)); } // 对静态资源文件映射支持 String staticPathPattern = this.mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration( registry.addResourceHandler(staticPathPattern) .addResourceLocations( this.resourceProperties.getStaticLocations()) .setCachePeriod(cachePeriod)); } } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**").addResourceLocations("file:"+"D://javasoftinstall/upload/"); registry.addResourceHandler("/**") .addResourceLocations("classpath:/resources/") .addResourceLocations("classpath:/static/") super.addResourceHandlers(registry); }
这里可以看到对静态资源处理的具体支持代码如下:
String staticPathPattern = this.mvcProperties.getStaticPathPattern(); if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration( registry.addResourceHandler(staticPathPattern) .addResourceLocations( this.resourceProperties.getStaticLocations()) .setCachePeriod(cachePeriod)); }
第一步拿到staticPathPattern;
/** * Path pattern used for static resources. */ private String staticPathPattern = "/**";
第二步,如果资源请求没有对应映射,就添加资源处理器及资源找寻位置并设置缓存
if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration( registry.addResourceHandler(staticPathPattern) .addResourceLocations( this.resourceProperties.getStaticLocations()) .setCachePeriod(cachePeriod)); }
其中 staticLocations在ResourceProperties类中,源码如下:
/** * Properties used to configure resource handling. */ @ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false) public class ResourceProperties implements ResourceLoaderAware { private static final String[] SERVLET_RESOURCE_LOCATIONS = { "/" }; //静态资源映射路径 private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" }; private static final String[] RESOURCE_LOCATIONS; static { RESOURCE_LOCATIONS = new String[CLASSPATH_RESOURCE_LOCATIONS.length + SERVLET_RESOURCE_LOCATIONS.length]; System.arraycopy(SERVLET_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, 0, SERVLET_RESOURCE_LOCATIONS.length); System.arraycopy(CLASSPATH_RESOURCE_LOCATIONS, 0, RESOURCE_LOCATIONS, SERVLET_RESOURCE_LOCATIONS.length, CLASSPATH_RESOURCE_LOCATIONS.length); } /** * Locations of static resources. Defaults to classpath:[/META-INF/resources/, * /resources/, /static/, /public/] plus context:/ (the root of the servlet context). */ private String[] staticLocations = RESOURCE_LOCATIONS;
即:"/**"
访问当前项目的任何资源,只要没有匹配的处理映射,则都去静态资源的文件夹找映射
"classpath:/META‐INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" "/":当前项目的根路径
如下图,访问static/asserts/img/bootstrap-solid.svg:
URL : http://localhost:8083/asserts/img/bootstrap-solid.svg;
这里注意URL上面不要添加静态资源文件夹(路径)的名字,如static、public等等。
【2】默认欢迎页的映射
代码如下:
@Bean public WelcomePageHandlerMapping welcomePageHandlerMapping( ResourceProperties resourceProperties) { return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(), this.mvcProperties.getStaticPathPattern()); }
注册了WelcomePageHandlerMappingBean来处理项目的默认欢迎页面,构造器有两个参数,第一个是项目中存在的欢迎页资源,第二个是静态资源路径映射规则。
① 获取欢迎页资源
其中跟踪resourceProperties.getWelcomePage()源码:
public Resource getWelcomePage() { for (String location : getStaticWelcomePageLocations()) { Resource resource = this.resourceLoader.getResource(location); try { if (resource.exists()) { resource.getURL(); return resource; } } catch (Exception ex) { // Ignore } } return null; }
即找到存在的欢迎页的资源,继续跟getStaticWelcomePageLocations()源码:
private String[] getStaticWelcomePageLocations() { String[] result = new String[this.staticLocations.length]; for (int i = 0; i < result.length; i++) { String location = this.staticLocations[i]; if (!location.endsWith("/")) { location = location + "/"; } result[i] = location + "index.html"; } return result; }
即 欢迎页的位置为第【1】部分中静态资源文件路径下的index.html。
如下所示:
"classpath:/META‐INF/resources/index.html", "classpath:/resources/index.html", "classpath:/static/index.html", "classpath:/public/index.html" "/":/index.html
② 静态路径映射规则
/** * Path pattern used for static resources. */ private String staticPathPattern = "/**";
③ WelcomePageHandlerMapping类
static final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping { private static final Log logger = LogFactory .getLog(WelcomePageHandlerMapping.class); private WelcomePageHandlerMapping(Resource welcomePage, String staticPathPattern) { if (welcomePage != null && "/**".equals(staticPathPattern)) { logger.info("Adding welcome page: " + welcomePage); ParameterizableViewController controller = new ParameterizableViewController(); controller.setViewName("forward:index.html"); setRootHandler(controller); setOrder(0); } } @Override public Object getHandlerInternal(HttpServletRequest request) throws Exception { for (MediaType mediaType : getAcceptedMediaTypes(request)) { if (mediaType.includes(MediaType.TEXT_HTML)) { return super.getHandlerInternal(request); } } return null; } private List<MediaType> getAcceptedMediaTypes(HttpServletRequest request) { String acceptHeader = request.getHeader(HttpHeaders.ACCEPT); return MediaType.parseMediaTypes( StringUtils.hasText(acceptHeader) ? acceptHeader : "*/*"); } }
总结:项目访问时,请求如http://localhost:8083/,则SpringBoot会从静态资源文件路径下找index.html,如果存在一个这样的资源,就转发请求到该页面。
【3】项目浏览器图标的映射
SpringBoot同样对项目浏览器图标请求映射做了默认配置。浏览器图片即项目访问时,浏览器页面上呈现的图标,如CSDN图标如下
代码如下:
@Configuration @ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true) public static class FaviconConfiguration { private final ResourceProperties resourceProperties; public FaviconConfiguration(ResourceProperties resourceProperties) { this.resourceProperties = resourceProperties; } @Bean public SimpleUrlHandlerMapping faviconHandlerMapping() { SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1); mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", faviconRequestHandler())); return mapping; } @Bean public ResourceHttpRequestHandler faviconRequestHandler() { ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler(); requestHandler .setLocations(this.resourceProperties.getFaviconLocations()); return requestHandler; } }
跟踪源码this.resourceProperties.getFaviconLocations()如下:
List<Resource> getFaviconLocations() { List<Resource> locations = new ArrayList<Resource>( this.staticLocations.length + 1); if (this.resourceLoader != null) { for (String location : this.staticLocations) { locations.add(this.resourceLoader.getResource(location)); } } locations.add(new ClassPathResource("/")); return Collections.unmodifiableList(locations); }
即在静态资源文件下找所有的favicon.ico !