commit
7d8f881c15
|
|
@ -21,9 +21,9 @@ import java.io.FileNotFoundException;
|
|||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.ContextResource;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
|
|
@ -32,7 +32,6 @@ import org.springframework.core.io.Resource;
|
|||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -153,9 +152,8 @@ public class ApplicationResourceLoader extends DefaultResourceLoader {
|
|||
* {@code spring.factories}. The factories file will be resolved using the default
|
||||
* class loader at the time this call is made.
|
||||
* @param resourceLoader the delegate resource loader
|
||||
* @param preferFileResolution if file based resolution is preferred over
|
||||
* {@code ServletContextResource} or {@link ClassPathResource} when no resource prefix
|
||||
* is provided.
|
||||
* @param preferFileResolution if file based resolution is preferred when a suitable
|
||||
* {@link ResourceFilePathResolver} support the resource
|
||||
* @return a {@link ResourceLoader} instance
|
||||
* @since 3.4.1
|
||||
*/
|
||||
|
|
@ -183,8 +181,10 @@ public class ApplicationResourceLoader extends DefaultResourceLoader {
|
|||
boolean preferFileResolution) {
|
||||
Assert.notNull(resourceLoader, "'resourceLoader' must not be null");
|
||||
Assert.notNull(springFactoriesLoader, "'springFactoriesLoader' must not be null");
|
||||
return new ProtocolResolvingResourceLoader(resourceLoader, springFactoriesLoader.load(ProtocolResolver.class),
|
||||
preferFileResolution);
|
||||
List<ProtocolResolver> protocolResolvers = springFactoriesLoader.load(ProtocolResolver.class);
|
||||
List<ResourceFilePathResolver> filePathResolvers = (preferFileResolution)
|
||||
? springFactoriesLoader.load(ResourceFilePathResolver.class) : Collections.emptyList();
|
||||
return new ProtocolResolvingResourceLoader(resourceLoader, protocolResolvers, filePathResolvers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -268,30 +268,22 @@ public class ApplicationResourceLoader extends DefaultResourceLoader {
|
|||
*/
|
||||
private static class ProtocolResolvingResourceLoader implements ResourceLoader {
|
||||
|
||||
private static final String SERVLET_CONTEXT_RESOURCE_CLASS_NAME = "org.springframework.web.context.support.ServletContextResource";
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
private final List<ProtocolResolver> protocolResolvers;
|
||||
|
||||
private final boolean preferFileResolution;
|
||||
|
||||
private final Class<?> servletContextResourceClass;
|
||||
private final List<ResourceFilePathResolver> filePathResolvers;
|
||||
|
||||
ProtocolResolvingResourceLoader(ResourceLoader resourceLoader, List<ProtocolResolver> protocolResolvers,
|
||||
boolean preferFileResolution) {
|
||||
List<ResourceFilePathResolver> filePathResolvers) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.protocolResolvers = protocolResolvers;
|
||||
this.preferFileResolution = preferFileResolution;
|
||||
this.servletContextResourceClass = resolveServletContextResourceClass(
|
||||
resourceLoader.getClass().getClassLoader());
|
||||
this.filePathResolvers = filePathResolvers;
|
||||
}
|
||||
|
||||
private static Class<?> resolveServletContextResourceClass(ClassLoader classLoader) {
|
||||
if (!ClassUtils.isPresent(SERVLET_CONTEXT_RESOURCE_CLASS_NAME, classLoader)) {
|
||||
return null;
|
||||
}
|
||||
return ClassUtils.resolveClassName(SERVLET_CONTEXT_RESOURCE_CLASS_NAME, classLoader);
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return this.resourceLoader.getClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -305,24 +297,18 @@ public class ApplicationResourceLoader extends DefaultResourceLoader {
|
|||
}
|
||||
}
|
||||
Resource resource = this.resourceLoader.getResource(location);
|
||||
if (this.preferFileResolution
|
||||
&& (isClassPathResourceByPath(location, resource) || isServletResource(resource))) {
|
||||
resource = new ApplicationResource(location);
|
||||
String fileSystemPath = getFileSystemPath(location, resource);
|
||||
return (fileSystemPath != null) ? new ApplicationResource(fileSystemPath) : resource;
|
||||
}
|
||||
|
||||
private String getFileSystemPath(String location, Resource resource) {
|
||||
for (ResourceFilePathResolver filePathResolver : this.filePathResolvers) {
|
||||
String filePath = filePathResolver.resolveFilePath(location, resource);
|
||||
if (filePath != null) {
|
||||
return filePath;
|
||||
}
|
||||
}
|
||||
return resource;
|
||||
}
|
||||
|
||||
private boolean isClassPathResourceByPath(String location, Resource resource) {
|
||||
return (resource instanceof ClassPathResource) && !location.startsWith(CLASSPATH_URL_PREFIX);
|
||||
}
|
||||
|
||||
private boolean isServletResource(Resource resource) {
|
||||
return this.servletContextResourceClass != null && this.servletContextResourceClass.isInstance(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return this.resourceLoader.getClassLoader();
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.io;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
/**
|
||||
* {@link ResourceFilePathResolver} for {@link ClassPathResource}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ClassPathResourceFilePathResolver implements ResourceFilePathResolver {
|
||||
|
||||
@Override
|
||||
public String resolveFilePath(String location, Resource resource) {
|
||||
return (resource instanceof ClassPathResource && !isClassPathUrl(location)) ? location : null;
|
||||
}
|
||||
|
||||
private boolean isClassPathUrl(String location) {
|
||||
return location.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.io;
|
||||
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* Strategy interface registered in {@code spring.factories} and used by
|
||||
* {@link ApplicationResourceLoader} to determine the file path of loaded resource when it
|
||||
* can also be represented as a {@link FileSystemResource}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 3.4.5
|
||||
*/
|
||||
public interface ResourceFilePathResolver {
|
||||
|
||||
/**
|
||||
* Return the {@code path} of the given resource if it can also be represented as a
|
||||
* {@link FileSystemResource}.
|
||||
* @param location the location used to create the resource
|
||||
* @param resource the resource to check
|
||||
* @return the file path of the resource or {@code null} if the it is not possible to
|
||||
* represent the resource as a {@link FileSystemResource}.
|
||||
*/
|
||||
String resolveFilePath(String location, Resource resource);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.context;
|
||||
|
||||
import org.springframework.boot.io.ResourceFilePathResolver;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.context.support.ServletContextResource;
|
||||
|
||||
/**
|
||||
* {@link ResourceFilePathResolver} for {@link ServletContextResource}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ServletContextResourceFilePathResolver implements ResourceFilePathResolver {
|
||||
|
||||
private static final String RESOURCE_CLASS_NAME = "org.springframework.web.context.support.ServletContextResource";
|
||||
|
||||
private final Class<?> resourceClass;
|
||||
|
||||
ServletContextResourceFilePathResolver() {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
this.resourceClass = ClassUtils.isPresent(RESOURCE_CLASS_NAME, classLoader)
|
||||
? ClassUtils.resolveClassName(RESOURCE_CLASS_NAME, classLoader) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String resolveFilePath(String location, Resource resource) {
|
||||
return (this.resourceClass != null && this.resourceClass.isInstance(resource)) ? location : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.reactive.context;
|
||||
|
||||
import org.springframework.boot.io.ResourceFilePathResolver;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* {@link ResourceFilePathResolver} for {@link FilteredReactiveWebContextResource}.
|
||||
*
|
||||
* @author Dmytro Nosan
|
||||
*/
|
||||
class FilteredReactiveWebContextResourceFilePathResolver implements ResourceFilePathResolver {
|
||||
|
||||
@Override
|
||||
public String resolveFilePath(String location, Resource resource) {
|
||||
return (resource instanceof FilteredReactiveWebContextResource) ? location : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -107,3 +107,9 @@ org.springframework.boot.sql.init.dependency.AnnotationDependsOnDatabaseInitiali
|
|||
# Resource Locator Protocol Resolvers
|
||||
org.springframework.core.io.ProtocolResolver=\
|
||||
org.springframework.boot.io.Base64ProtocolResolver
|
||||
|
||||
# Resource File Path Resolvers
|
||||
org.springframework.boot.io.ResourceFilePathResolver=\
|
||||
org.springframework.boot.io.ClassPathResourceFilePathResolver,\
|
||||
org.springframework.boot.web.context.ServletContextResourceFilePathResolver,\
|
||||
org.springframework.boot.web.reactive.context.FilteredReactiveWebContextResourceFilePathResolver
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import java.util.Base64;
|
|||
import java.util.Enumeration;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.testsupport.classpath.resources.ResourcePath;
|
||||
|
|
@ -37,9 +36,6 @@ import org.springframework.core.io.FileSystemResource;
|
|||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.context.support.ServletContextResource;
|
||||
import org.springframework.web.context.support.ServletContextResourceLoader;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
|
@ -315,19 +311,6 @@ class ApplicationResourceLoaderTests {
|
|||
assertThat(resource).isInstanceOf(ClassPathResource.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceWithPreferFileResolutionWhenPathWithServletContextResource() throws Exception {
|
||||
ServletContext servletContext = new MockServletContext();
|
||||
ServletContextResourceLoader servletContextResourceLoader = new ServletContextResourceLoader(servletContext);
|
||||
ResourceLoader loader = ApplicationResourceLoader.get(servletContextResourceLoader, true);
|
||||
Resource resource = loader.getResource("src/main/resources/a-file");
|
||||
assertThat(resource).isInstanceOf(FileSystemResource.class);
|
||||
assertThat(resource.getFile().getAbsoluteFile())
|
||||
.isEqualTo(new File("src/main/resources/a-file").getAbsoluteFile());
|
||||
ResourceLoader regularLoader = ApplicationResourceLoader.get(servletContextResourceLoader, false);
|
||||
assertThat(regularLoader.getResource("src/main/resources/a-file")).isInstanceOf(ServletContextResource.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getClassLoaderReturnsDelegateClassLoader() {
|
||||
ClassLoader classLoader = new TestClassLoader(this::useTestProtocolResolversFactories);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.context;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.io.ApplicationResourceLoader;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.context.support.ServletContextResource;
|
||||
import org.springframework.web.context.support.ServletContextResourceLoader;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link ServletContextResourceFilePathResolver}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ServletContextResourceFilePathResolverIntegrationTests {
|
||||
|
||||
@Test
|
||||
void getResourceWithPreferFileResolutionWhenPathWithServletContextResource() throws Exception {
|
||||
ServletContext servletContext = new MockServletContext();
|
||||
ServletContextResourceLoader servletContextResourceLoader = new ServletContextResourceLoader(servletContext);
|
||||
ResourceLoader loader = ApplicationResourceLoader.get(servletContextResourceLoader, true);
|
||||
Resource resource = loader.getResource("src/main/resources/a-file");
|
||||
assertThat(resource).isInstanceOf(FileSystemResource.class);
|
||||
assertThat(resource.getFile().getAbsoluteFile())
|
||||
.isEqualTo(new File("src/main/resources/a-file").getAbsoluteFile());
|
||||
ResourceLoader regularLoader = ApplicationResourceLoader.get(servletContextResourceLoader, false);
|
||||
assertThat(regularLoader.getResource("src/main/resources/a-file")).isInstanceOf(ServletContextResource.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.reactive.context;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.io.ApplicationResourceLoader;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link FilteredReactiveWebContextResourceFilePathResolver}.
|
||||
*
|
||||
* @author Dmytro Nosan
|
||||
*/
|
||||
class FilteredReactiveWebContextResourceFilePathResolverIntegrationTests {
|
||||
|
||||
@Test
|
||||
void getResourceWhenFilteredReactiveWebContextResourceWithPreferFileResolution() throws Exception {
|
||||
ResourceLoader resourceLoader = ApplicationResourceLoader
|
||||
.get(new AnnotationConfigReactiveWebApplicationContext(), true);
|
||||
Resource resource = resourceLoader.getResource("src/main/resources/a-file");
|
||||
assertThat(resource).isInstanceOf(FileSystemResource.class);
|
||||
assertThat(resource.getFile().getAbsoluteFile())
|
||||
.isEqualTo(new File("src/main/resources/a-file").getAbsoluteFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceWhenFilteredReactiveWebContextResource() {
|
||||
ResourceLoader resourceLoader = ApplicationResourceLoader
|
||||
.get(new AnnotationConfigReactiveWebApplicationContext(), false);
|
||||
Resource resource = resourceLoader.getResource("src/main/resources/a-file");
|
||||
assertThat(resource).isInstanceOf(ClassUtils.resolveClassName(
|
||||
"org.springframework.boot.web.reactive.context.FilteredReactiveWebContextResource", null));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue