Skip to content

Commit 9868c28

Browse files
committed
Honor ProtocolResolvers in GenericApplicationContext
When the ProtocolResolver SPI was introduced in Spring Framework 4.3, support for protocol resolvers was added in DefaultResourceLoader's getResource() implementation; however, GenericApplicationContext's overridden getResource() implementation was not updated accordingly. Prior to this commit, if a GenericApplicationContext was configured with a custom ResourceLoader, registered protocol resolvers were ignored. This commit ensures that protocol resolvers are honored in GenericApplicationContext even if a custom ResourceLoader is used. Closes gh-28703
1 parent a970516 commit 9868c28

File tree

3 files changed

+78
-7
lines changed

3 files changed

+78
-7
lines changed

spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@
3333
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
3434
import org.springframework.beans.factory.support.RootBeanDefinition;
3535
import org.springframework.context.ApplicationContext;
36+
import org.springframework.core.io.ProtocolResolver;
3637
import org.springframework.core.io.Resource;
3738
import org.springframework.core.io.ResourceLoader;
3839
import org.springframework.core.io.support.ResourcePatternResolver;
@@ -86,6 +87,7 @@
8687
*
8788
* @author Juergen Hoeller
8889
* @author Chris Beams
90+
* @author Sam Brannen
8991
* @since 1.1.2
9092
* @see #registerBeanDefinition
9193
* @see #refresh()
@@ -216,13 +218,23 @@ public void setResourceLoader(ResourceLoader resourceLoader) {
216218
//---------------------------------------------------------------------
217219

218220
/**
219-
* This implementation delegates to this context's ResourceLoader if set,
220-
* falling back to the default superclass behavior else.
221-
* @see #setResourceLoader
221+
* This implementation delegates to this context's {@code ResourceLoader} if set,
222+
* falling back to the default superclass behavior otherwise.
223+
* <p>As of Spring Framework 5.3.22, this method also honors registered
224+
* {@linkplain #getProtocolResolvers() protocol resolvers} when a custom
225+
* {@code ResourceLoader} has been set.
226+
* @see #setResourceLoader(ResourceLoader)
227+
* @see #addProtocolResolver(ProtocolResolver)
222228
*/
223229
@Override
224230
public Resource getResource(String location) {
225231
if (this.resourceLoader != null) {
232+
for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
233+
Resource resource = protocolResolver.resolve(location, this);
234+
if (resource != null) {
235+
return resource;
236+
}
237+
}
226238
return this.resourceLoader.getResource(location);
227239
}
228240
return super.getResource(location);
@@ -231,7 +243,7 @@ public Resource getResource(String location) {
231243
/**
232244
* This implementation delegates to this context's ResourceLoader if it
233245
* implements the ResourcePatternResolver interface, falling back to the
234-
* default superclass behavior else.
246+
* default superclass behavior otherwise.
235247
* @see #setResourceLoader
236248
*/
237249
@Override

spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,28 @@
2424
import org.springframework.beans.factory.support.RootBeanDefinition;
2525
import org.springframework.context.ApplicationContext;
2626
import org.springframework.context.ApplicationContextAware;
27+
import org.springframework.core.io.ByteArrayResource;
28+
import org.springframework.core.io.ClassPathResource;
29+
import org.springframework.core.io.FileSystemResource;
30+
import org.springframework.core.io.FileSystemResourceLoader;
31+
import org.springframework.core.io.FileUrlResource;
32+
import org.springframework.core.io.ProtocolResolver;
33+
import org.springframework.core.io.Resource;
34+
import org.springframework.core.io.ResourceLoader;
2735
import org.springframework.core.metrics.jfr.FlightRecorderApplicationStartup;
2836

37+
import static java.nio.charset.StandardCharsets.UTF_8;
2938
import static org.assertj.core.api.Assertions.assertThat;
3039
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
3140
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
41+
import static org.assertj.core.api.InstanceOfAssertFactories.type;
3242

3343
/**
44+
* Tests for {@link GenericApplicationContext}.
45+
*
3446
* @author Juergen Hoeller
3547
* @author Chris Beams
48+
* @author Sam Brannen
3649
*/
3750
class GenericApplicationContextTests {
3851

@@ -209,6 +222,39 @@ void configureApplicationStartupOnBeanFactory() {
209222
assertThat(context.getBeanFactory().getApplicationStartup()).isEqualTo(applicationStartup);
210223
}
211224

225+
@Test
226+
void getResourceWithDefaultResourceLoader() {
227+
assertGetResourceSemantics(null, ClassPathResource.class);
228+
}
229+
230+
@Test
231+
void getResourceWithCustomResourceLoader() {
232+
assertGetResourceSemantics(new FileSystemResourceLoader(), FileSystemResource.class);
233+
}
234+
235+
private void assertGetResourceSemantics(ResourceLoader resourceLoader, Class<? extends Resource> defaultResouceType) {
236+
if (resourceLoader != null) {
237+
context.setResourceLoader(resourceLoader);
238+
}
239+
240+
String pingLocation = "ping:foo";
241+
String fileLocation = "file:foo";
242+
243+
Resource resource = context.getResource(pingLocation);
244+
assertThat(resource).isInstanceOf(defaultResouceType);
245+
resource = context.getResource(fileLocation);
246+
assertThat(resource).isInstanceOf(FileUrlResource.class);
247+
248+
context.addProtocolResolver(new PingPongProtocolResolver());
249+
250+
resource = context.getResource(pingLocation);
251+
assertThat(resource).asInstanceOf(type(ByteArrayResource.class))
252+
.extracting(bar -> new String(bar.getByteArray(), UTF_8))
253+
.isEqualTo("pong:foo");
254+
resource = context.getResource(fileLocation);
255+
assertThat(resource).isInstanceOf(FileUrlResource.class);
256+
}
257+
212258

213259
static class BeanA {
214260

@@ -236,4 +282,15 @@ public void setApplicationContext(ApplicationContext applicationContext) {
236282

237283
static class BeanC {}
238284

285+
static class PingPongProtocolResolver implements ProtocolResolver {
286+
287+
@Override
288+
public Resource resolve(String location, ResourceLoader resourceLoader) {
289+
if (location.startsWith("ping:")) {
290+
return new ByteArrayResource(("pong:" + location.substring(5)).getBytes(UTF_8));
291+
}
292+
return null;
293+
}
294+
}
295+
239296
}

spring-core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -32,7 +32,8 @@
3232

3333
/**
3434
* Default implementation of the {@link ResourceLoader} interface.
35-
* Used by {@link ResourceEditor}, and serves as base class for
35+
*
36+
* <p>Used by {@link ResourceEditor}, and serves as base class for
3637
* {@link org.springframework.context.support.AbstractApplicationContext}.
3738
* Can also be used standalone.
3839
*
@@ -114,6 +115,7 @@ public void addProtocolResolver(ProtocolResolver resolver) {
114115
* Return the collection of currently registered protocol resolvers,
115116
* allowing for introspection as well as modification.
116117
* @since 4.3
118+
* @see #addProtocolResolver(ProtocolResolver)
117119
*/
118120
public Collection<ProtocolResolver> getProtocolResolvers() {
119121
return this.protocolResolvers;

0 commit comments

Comments
 (0)