springboot配置靜態資源方式是多種多樣,接下來我會介紹其中幾種方式,並解析一下其中的原理。jquery
應該說 spring.mvc.static-path-pattern 和 spring.resources.static-locations這兩屬性是成對使用的,若是不明白其中的原理,總會出現資源404的狀況。首先收一下spring.mvc.static-path-pattern表明的是一個Ant Path路徑,例如resources/**,表示當你的路徑中存在resources/**的時候纔會處理請求。好比咱們訪問「http://localhost:8080/resources/xxx.js」時,很顯然,springboot邏輯中會根據模式匹配對url進行匹配,匹配命中後,是如何再定位到具體的資源的呢?這時候spring.resources.static-locations的配置就起做用了。web
忘記說了,在springboot中spring.mvc.static-path-pattern的默認值是/**,spring.resources.static-locations的默認值是classpath:/static,classpath:/public,classpath:/resources,classpath:/META-INF/resources,servlet context:/,springboot中相關的ResourceHttpRequestHandler就會去spring.resources.static-locations配置的全部路徑中尋找資源文件。spring
因此我以前才說spring.mvc.static-path-pattern 和 spring.resources.static-locations這兩屬性是成對使用的。springboot
調試過程當中,經過查看 org.springframework.web.servlet.DispatcherServlet中的handlerMappings變量,咱們發現有一個很顯眼的 resourceHandlerMapping ,這個是springboot爲咱們提供的一個默認的靜態資源handler,經過全文搜索發現出如今org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport這個類中,也就是這個類包含了@EnableWebMvc註解中的大多數功能,更多的擴展功能請參考org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration。mvc
resourceHandlerMapping 的定義以下。app
/** * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped * resource handlers. To configure resource handling, override * {@link #addResourceHandlers}. */ @Bean public HandlerMapping resourceHandlerMapping() { ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext, this.servletContext, mvcContentNegotiationManager()); addResourceHandlers(registry); AbstractHandlerMapping handlerMapping = registry.getHandlerMapping(); if (handlerMapping != null) { handlerMapping.setPathMatcher(mvcPathMatcher()); handlerMapping.setUrlPathHelper(mvcUrlPathHelper()); handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider())); handlerMapping.setCorsConfigurations(getCorsConfigurations()); } else { handlerMapping = new EmptyHandlerMapping(); } return handlerMapping; }
請你們先記住ResourceHandlerRegistry這個類。ide
首先看一下addResourceHandlers(registry);這個方法,父類DelegatingWebMvcConfiguration作了實現,以下。this
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
@Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { this.configurers.addResourceHandlers(registry); }
其中WebMvcConfigurerComposite是操做了WebMvcConfigurer類型的對象的集合。在org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration這個springmvc的自動配置類中,有一個WebMvcConfigurer的實現類,以下。url
// Defined as a nested config to ensure WebMvcConfigurerAdapter is not read when not // on the classpath @Configuration @Import(EnableWebMvcConfiguration.class) @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) public static class WebMvcAutoConfigurationAdapter extends WebMvcConfigurerAdapter { ... @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } 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)); } } ... }
上面的addResourceHandlers方法中,增長了默認的mapping pattern = /webjars/** ,默認的resource location是classpath:/META-INF/resources/webjars/。正是這裏的配置,咱們在集成swagger的時候,就能夠正常訪問到swagger webjars中的js文件了。其中紅色的代碼部分就是用戶能夠自定義的默認靜態資源訪問方式,並經過ResourceHandlerRegistry對象進行註冊。接着看一下mvcProperties和resourceProperties對應的類吧。spa
@ConfigurationProperties("spring.mvc") public class WebMvcProperties { ... /** * Path pattern used for static resources. */ private String staticPathPattern = "/**"; ... }
WebMvcProperties類中的staticPathPattern field 對應了spring.mvc.static-path-pattern這個屬性,能夠看到默認值是 "/**"。
@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); } private String[] staticLocations = RESOURCE_LOCATIONS; ...... }
ResourceProperties中staticLocations field 對應了 spring.resources.static-locations 這個屬性。能夠看到默認值是classpath:[/META-INF/resources/, /resources/, /static/, /public/], servlet context:/
在瞭解了springboot默認資源的配置的原理(即 spring.mvc.static-path-pattern 和 spring.resources.static-locations),咱們能夠增長一個WebMvcConfigurer類型的bean,來添加靜態資源的訪問方式,還記得上面說的「請記住ResourceHandlerRegistry這個類「,下面就用到了哦。
@Configuration public class ResourceWebMvcConfigurer extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") .addResourceLocations("classpath:/public-resources/") .setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic()); } }
那麼當訪問路徑中包含"resources/**"的時候,resource handler就會去classpath:/public-resources目錄下尋找了。
參考 org.springframework.web.servlet.resource.ResourceHttpRequestHandler,ResourceHttpRequestHandler中經過org.springframework.web.servlet.resource.PathResourceResolver進行查找。
舉個例子,下圖是springboot打包以後的目錄結構,如今想要經過url訪問application.properties文件,springboot默認的靜態文件配置能夠嗎?固然須要用事實來講話了。
咱們已經知道,默認的resource locations中有個 servlet-context:/,訪問你的url是http://localhost:8080/工程名/application.properties,調試一下PathResourceResolver,結果以下。
發現servlet-context的根路徑如上圖所示,查看一下這個路徑對應的目錄,發現什麼都沒有,因此很顯然沒法找到咱們要找的文件了。畢竟通常使用springboot都是jar項目,servlet-context path下沒有用戶自定義的資源。
在Servlet3協議規範中,包含在JAR文件/META-INFO/resources/路徑下的資源能夠直接訪問了。若是將springboot項目打包成war包,能夠配置一個默認的servlet。在WebMvcConfigurationSupport中已經定義好了,不過默認是一個EmptyHandlerMapping。
/** * Return a handler mapping ordered at Integer.MAX_VALUE with a mapped * default servlet handler. To configure "default" Servlet handling, * override {@link #configureDefaultServletHandling}. */ @Bean public HandlerMapping defaultServletHandlerMapping() { DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(servletContext); configureDefaultServletHandling(configurer); AbstractHandlerMapping handlerMapping = configurer.getHandlerMapping(); handlerMapping = handlerMapping != null ? handlerMapping : new EmptyHandlerMapping(); return handlerMapping; }
能夠經過自定義一個WebMvcConfigurer類型的bean,改寫configureDefaultServletHandling 方法,以下。
@Configuration public class MyWebConfigurer extends WebMvcConfigurerAdapter { @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }
這樣就設置了一個默認的servlet,在加載靜態資源的時候就會按照servelt方式去加載了。
就先分享這麼多了,更多分享請關注咱們的技術公衆號吧!!!