Spring Boot 默认的source文件路径:
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
这个从之前的版本就一致如此定义,只是since2.4.x之后,ResourceProperties被打上了@Deprecated标识,并且明确的指引到:
Deprecated
since 2.4.0 for removal in 2.6.0 in favor of WebProperties.Resources最新的项目,使用的是2.6.x的版本,故已经没有ResourceProperties文件了,需要找的话,可以去WebProperties.Resources静态内部类找
public static class Resources { private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" }; /** * Locations of static resources. Defaults to classpath:[/META-INF/resources/, * /resources/, /static/, /public/]. */ private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS; /** * Whether to enable default resource handling. */ private boolean addMappings = true; private boolean customized = false; private final Chain chain = new Chain(); private final Cache cache = new Cache(); public String[] getStaticLocations() { return this.staticLocations; } public void setStaticLocations(String[] staticLocations) { this.staticLocations = appendSlashIfNecessary(staticLocations); this.customized = true; } private String[] appendSlashIfNecessary(String[] staticLocations) { String[] normalized = new String[staticLocations.length]; for (int i = 0; i < staticLocations.length; i++) { String location = staticLocations[i]; normalized[i] = location.endsWith("/") ? location : location + "/"; } return normalized; } public boolean isAddMappings() { return this.addMappings; } public void setAddMappings(boolean addMappings) { this.customized = true; this.addMappings = addMappings; } public Chain getChain() { return this.chain; } public Cache getCache() { return this.cache; } public boolean hasBeenCustomized() { return this.customized || getChain().hasBeenCustomized() || getCache().hasBeenCustomized(); } /** * Configuration for the Spring Resource Handling chain. */ public static class Chain { boolean customized = false; /** * Whether to enable the Spring Resource Handling chain. By default, disabled * unless at least one strategy has been enabled. */ private Boolean enabled; /** * Whether to enable caching in the Resource chain. */ private boolean cache = true; /** * Whether to enable resolution of already compressed resources (gzip, * brotli). Checks for a resource name with the '.gz' or '.br' file * extensions. */ private boolean compressed = false; private final Strategy strategy = new Strategy(); /** * Return whether the resource chain is enabled. Return {@code null} if no * specific settings are present. * @return whether the resource chain is enabled or {@code null} if no * specified settings are present. */ public Boolean getEnabled() { return getEnabled(getStrategy().getFixed().isEnabled(), getStrategy().getContent().isEnabled(), this.enabled); } private boolean hasBeenCustomized() { return this.customized || getStrategy().hasBeenCustomized(); } public void setEnabled(boolean enabled) { this.enabled = enabled; this.customized = true; } public boolean isCache() { return this.cache; } public void setCache(boolean cache) { this.cache = cache; this.customized = true; } public Strategy getStrategy() { return this.strategy; } public boolean isCompressed() { return this.compressed; } public void setCompressed(boolean compressed) { this.compressed = compressed; this.customized = true; } static Boolean getEnabled(boolean fixedEnabled, boolean contentEnabled, Boolean chainEnabled) { return (fixedEnabled || contentEnabled) ? Boolean.TRUE : chainEnabled; } /** * Strategies for extracting and embedding a resource version in its URL path. */ public static class Strategy { private final Fixed fixed = new Fixed(); private final Content content = new Content(); public Fixed getFixed() { return this.fixed; } public Content getContent() { return this.content; } private boolean hasBeenCustomized() { return getFixed().hasBeenCustomized() || getContent().hasBeenCustomized(); } /** * Version Strategy based on content hashing. */ public static class Content { private boolean customized = false; /** * Whether to enable the content Version Strategy. */ private boolean enabled; /** * Comma-separated list of patterns to apply to the content Version * Strategy. */ private String[] paths = new String[] { "/**" }; public boolean isEnabled() { return this.enabled; } public void setEnabled(boolean enabled) { this.customized = true; this.enabled = enabled; } public String[] getPaths() { return this.paths; } public void setPaths(String[] paths) { this.customized = true; this.paths = paths; } private boolean hasBeenCustomized() { return this.customized; } } /** * Version Strategy based on a fixed version string. */ public static class Fixed { private boolean customized = false; /** * Whether to enable the fixed Version Strategy. */ private boolean enabled; /** * Comma-separated list of patterns to apply to the fixed Version * Strategy. */ private String[] paths = new String[] { "/**" }; /** * Version string to use for the fixed Version Strategy. */ private String version; public boolean isEnabled() { return this.enabled; } public void setEnabled(boolean enabled) { this.customized = true; this.enabled = enabled; } public String[] getPaths() { return this.paths; } public void setPaths(String[] paths) { this.customized = true; this.paths = paths; } public String getVersion() { return this.version; } public void setVersion(String version) { this.customized = true; this.version = version; } private boolean hasBeenCustomized() { return this.customized; } } } } /** * Cache configuration. */ public static class Cache { private boolean customized = false; /** * Cache period for the resources served by the resource handler. If a * duration suffix is not specified, seconds will be used. Can be overridden * by the 'spring.web.resources.cache.cachecontrol' properties. */ @DurationUnit(ChronoUnit.SECONDS) private Duration period; /** * Cache control HTTP headers, only allows valid directive combinations. * Overrides the 'spring.web.resources.cache.period' property. */ private final Cachecontrol cachecontrol = new Cachecontrol(); /** * Whether we should use the "lastModified" metadata of the files in HTTP * caching headers. */ private boolean useLastModified = true; public Duration getPeriod() { return this.period; } public void setPeriod(Duration period) { this.customized = true; this.period = period; } public Cachecontrol getCachecontrol() { return this.cachecontrol; } public boolean isUseLastModified() { return this.useLastModified; } public void setUseLastModified(boolean useLastModified) { this.useLastModified = useLastModified; } private boolean hasBeenCustomized() { return this.customized || getCachecontrol().hasBeenCustomized(); } /** * Cache Control HTTP header configuration. */ public static class Cachecontrol { private boolean customized = false; /** * Maximum time the response should be cached, in seconds if no duration * suffix is not specified. */ @DurationUnit(ChronoUnit.SECONDS) private Duration maxAge; /** * Indicate that the cached response can be reused only if re-validated * with the server. */ private Boolean noCache; /** * Indicate to not cache the response in any case. */ private Boolean noStore; /** * Indicate that once it has become stale, a cache must not use the * response without re-validating it with the server. */ private Boolean mustRevalidate; /** * Indicate intermediaries (caches and others) that they should not * transform the response content. */ private Boolean noTransform; /** * Indicate that any cache may store the response. */ private Boolean cachePublic; /** * Indicate that the response message is intended for a single user and * must not be stored by a shared cache. */ private Boolean cachePrivate; /** * Same meaning as the "must-revalidate" directive, except that it does * not apply to private caches. */ private Boolean proxyRevalidate; /** * Maximum time the response can be served after it becomes stale, in * seconds if no duration suffix is not specified. */ @DurationUnit(ChronoUnit.SECONDS) private Duration staleWhileRevalidate; /** * Maximum time the response may be used when errors are encountered, in * seconds if no duration suffix is not specified. */ @DurationUnit(ChronoUnit.SECONDS) private Duration staleIfError; /** * Maximum time the response should be cached by shared caches, in seconds * if no duration suffix is not specified. */ @DurationUnit(ChronoUnit.SECONDS) private Duration sMaxAge; public Duration getMaxAge() { return this.maxAge; } public void setMaxAge(Duration maxAge) { this.customized = true; this.maxAge = maxAge; } public Boolean getNoCache() { return this.noCache; } public void setNoCache(Boolean noCache) { this.customized = true; this.noCache = noCache; } public Boolean getNoStore() { return this.noStore; } public void setNoStore(Boolean noStore) { this.customized = true; this.noStore = noStore; } public Boolean getMustRevalidate() { return this.mustRevalidate; } public void setMustRevalidate(Boolean mustRevalidate) { this.customized = true; this.mustRevalidate = mustRevalidate; } public Boolean getNoTransform() { return this.noTransform; } public void setNoTransform(Boolean noTransform) { this.customized = true; this.noTransform = noTransform; } public Boolean getCachePublic() { return this.cachePublic; } public void setCachePublic(Boolean cachePublic) { this.customized = true; this.cachePublic = cachePublic; } public Boolean getCachePrivate() { return this.cachePrivate; } public void setCachePrivate(Boolean cachePrivate) { this.customized = true; this.cachePrivate = cachePrivate; } public Boolean getProxyRevalidate() { return this.proxyRevalidate; } public void setProxyRevalidate(Boolean proxyRevalidate) { this.customized = true; this.proxyRevalidate = proxyRevalidate; } public Duration getStaleWhileRevalidate() { return this.staleWhileRevalidate; } public void setStaleWhileRevalidate(Duration staleWhileRevalidate) { this.customized = true; this.staleWhileRevalidate = staleWhileRevalidate; } public Duration getStaleIfError() { return this.staleIfError; } public void setStaleIfError(Duration staleIfError) { this.customized = true; this.staleIfError = staleIfError; } public Duration getSMaxAge() { return this.sMaxAge; } public void setSMaxAge(Duration sMaxAge) { this.customized = true; this.sMaxAge = sMaxAge; } public CacheControl toHttpCacheControl() { PropertyMapper map = PropertyMapper.get(); CacheControl control = createCacheControl(); map.from(this::getMustRevalidate).whenTrue().toCall(control::mustRevalidate); map.from(this::getNoTransform).whenTrue().toCall(control::noTransform); map.from(this::getCachePublic).whenTrue().toCall(control::cachePublic); map.from(this::getCachePrivate).whenTrue().toCall(control::cachePrivate); map.from(this::getProxyRevalidate).whenTrue().toCall(control::proxyRevalidate); map.from(this::getStaleWhileRevalidate).whenNonNull() .to((duration) -> control.staleWhileRevalidate(duration.getSeconds(), TimeUnit.SECONDS)); map.from(this::getStaleIfError).whenNonNull() .to((duration) -> control.staleIfError(duration.getSeconds(), TimeUnit.SECONDS)); map.from(this::getSMaxAge).whenNonNull() .to((duration) -> control.sMaxAge(duration.getSeconds(), TimeUnit.SECONDS)); // check if cacheControl remained untouched if (control.getHeaderValue() == null) { return null; } return control; } private CacheControl createCacheControl() { if (Boolean.TRUE.equals(this.noStore)) { return CacheControl.noStore(); } if (Boolean.TRUE.equals(this.noCache)) { return CacheControl.noCache(); } if (this.maxAge != null) { return CacheControl.maxAge(this.maxAge.getSeconds(), TimeUnit.SECONDS); } return CacheControl.empty(); } private boolean hasBeenCustomized() { return this.customized; } } } }