diff --git a/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java index accdb15031a..19120cbfa81 100644 --- a/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 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. @@ -33,6 +33,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; +import org.springframework.core.io.ProtocolResolver; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.ResourcePatternResolver; @@ -86,6 +87,7 @@ import org.springframework.util.Assert; * * @author Juergen Hoeller * @author Chris Beams + * @author Sam Brannen * @since 1.1.2 * @see #registerBeanDefinition * @see #refresh() @@ -216,13 +218,23 @@ public class GenericApplicationContext extends AbstractApplicationContext implem //--------------------------------------------------------------------- /** - * This implementation delegates to this context's ResourceLoader if set, - * falling back to the default superclass behavior else. - * @see #setResourceLoader + * This implementation delegates to this context's {@code ResourceLoader} if set, + * falling back to the default superclass behavior otherwise. + *
As of Spring Framework 5.3.22, this method also honors registered + * {@linkplain #getProtocolResolvers() protocol resolvers} when a custom + * {@code ResourceLoader} has been set. + * @see #setResourceLoader(ResourceLoader) + * @see #addProtocolResolver(ProtocolResolver) */ @Override public Resource getResource(String location) { if (this.resourceLoader != null) { + for (ProtocolResolver protocolResolver : getProtocolResolvers()) { + Resource resource = protocolResolver.resolve(location, this); + if (resource != null) { + return resource; + } + } return this.resourceLoader.getResource(location); } return super.getResource(location); @@ -231,7 +243,7 @@ public class GenericApplicationContext extends AbstractApplicationContext implem /** * This implementation delegates to this context's ResourceLoader if it * implements the ResourcePatternResolver interface, falling back to the - * default superclass behavior else. + * default superclass behavior otherwise. * @see #setResourceLoader */ @Override diff --git a/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java index cadce607bbe..5a656f14a90 100644 --- a/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java +++ b/spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java @@ -24,15 +24,28 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.FileSystemResourceLoader; +import org.springframework.core.io.FileUrlResource; +import org.springframework.core.io.ProtocolResolver; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; import org.springframework.core.metrics.jfr.FlightRecorderApplicationStartup; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.InstanceOfAssertFactories.type; /** + * Tests for {@link GenericApplicationContext}. + * * @author Juergen Hoeller * @author Chris Beams + * @author Sam Brannen */ class GenericApplicationContextTests { @@ -209,6 +222,39 @@ class GenericApplicationContextTests { assertThat(context.getBeanFactory().getApplicationStartup()).isEqualTo(applicationStartup); } + @Test + void getResourceWithDefaultResourceLoader() { + assertGetResourceSemantics(null, ClassPathResource.class); + } + + @Test + void getResourceWithCustomResourceLoader() { + assertGetResourceSemantics(new FileSystemResourceLoader(), FileSystemResource.class); + } + + private void assertGetResourceSemantics(ResourceLoader resourceLoader, Class extends Resource> defaultResouceType) { + if (resourceLoader != null) { + context.setResourceLoader(resourceLoader); + } + + String pingLocation = "ping:foo"; + String fileLocation = "file:foo"; + + Resource resource = context.getResource(pingLocation); + assertThat(resource).isInstanceOf(defaultResouceType); + resource = context.getResource(fileLocation); + assertThat(resource).isInstanceOf(FileUrlResource.class); + + context.addProtocolResolver(new PingPongProtocolResolver()); + + resource = context.getResource(pingLocation); + assertThat(resource).asInstanceOf(type(ByteArrayResource.class)) + .extracting(bar -> new String(bar.getByteArray(), UTF_8)) + .isEqualTo("pong:foo"); + resource = context.getResource(fileLocation); + assertThat(resource).isInstanceOf(FileUrlResource.class); + } + static class BeanA { @@ -236,4 +282,15 @@ class GenericApplicationContextTests { static class BeanC {} + static class PingPongProtocolResolver implements ProtocolResolver { + + @Override + public Resource resolve(String location, ResourceLoader resourceLoader) { + if (location.startsWith("ping:")) { + return new ByteArrayResource(("pong:" + location.substring(5)).getBytes(UTF_8)); + } + return null; + } + } + } diff --git a/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java b/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java index 27ed3bf1a08..ef24b3803e6 100644 --- a/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 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. @@ -32,7 +32,8 @@ import org.springframework.util.StringUtils; /** * Default implementation of the {@link ResourceLoader} interface. - * Used by {@link ResourceEditor}, and serves as base class for + * + *
Used by {@link ResourceEditor}, and serves as base class for
* {@link org.springframework.context.support.AbstractApplicationContext}.
* Can also be used standalone.
*
@@ -114,6 +115,7 @@ public class DefaultResourceLoader implements ResourceLoader {
* Return the collection of currently registered protocol resolvers,
* allowing for introspection as well as modification.
* @since 4.3
+ * @see #addProtocolResolver(ProtocolResolver)
*/
public Collection