Skip to content

Commit af16e49

Browse files
committed
Merge branch '5.3.x'
# Conflicts: # spring-context/src/main/java/org/springframework/context/support/GenericApplicationContext.java # spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java
2 parents 5e5e95a + 9868c28 commit af16e49

File tree

3 files changed

+129
-71
lines changed

3 files changed

+129
-71
lines changed

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
3636
import org.springframework.beans.factory.support.RootBeanDefinition;
3737
import org.springframework.context.ApplicationContext;
38+
import org.springframework.core.io.ProtocolResolver;
3839
import org.springframework.core.io.Resource;
3940
import org.springframework.core.io.ResourceLoader;
4041
import org.springframework.core.io.support.ResourcePatternResolver;
@@ -93,6 +94,7 @@
9394
* @author Juergen Hoeller
9495
* @author Chris Beams
9596
* @author Stephane Nicoll
97+
* @author Sam Brannen
9698
* @since 1.1.2
9799
* @see #registerBeanDefinition
98100
* @see #refresh()
@@ -223,13 +225,23 @@ public void setResourceLoader(ResourceLoader resourceLoader) {
223225
//---------------------------------------------------------------------
224226

225227
/**
226-
* This implementation delegates to this context's ResourceLoader if set,
227-
* falling back to the default superclass behavior else.
228-
* @see #setResourceLoader
228+
* This implementation delegates to this context's {@code ResourceLoader} if set,
229+
* falling back to the default superclass behavior otherwise.
230+
* <p>As of Spring Framework 5.3.22, this method also honors registered
231+
* {@linkplain #getProtocolResolvers() protocol resolvers} when a custom
232+
* {@code ResourceLoader} has been set.
233+
* @see #setResourceLoader(ResourceLoader)
234+
* @see #addProtocolResolver(ProtocolResolver)
229235
*/
230236
@Override
231237
public Resource getResource(String location) {
232238
if (this.resourceLoader != null) {
239+
for (ProtocolResolver protocolResolver : getProtocolResolvers()) {
240+
Resource resource = protocolResolver.resolve(location, this);
241+
if (resource != null) {
242+
return resource;
243+
}
244+
}
233245
return this.resourceLoader.getResource(location);
234246
}
235247
return super.getResource(location);
@@ -238,7 +250,7 @@ public Resource getResource(String location) {
238250
/**
239251
* This implementation delegates to this context's ResourceLoader if it
240252
* implements the ResourcePatternResolver interface, falling back to the
241-
* default superclass behavior else.
253+
* default superclass behavior otherwise.
242254
* @see #setResourceLoader
243255
*/
244256
@Override

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

Lines changed: 109 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.context.support;
1818

19+
import org.junit.jupiter.api.AfterEach;
1920
import org.junit.jupiter.api.Test;
2021
import org.mockito.ArgumentCaptor;
2122

@@ -31,90 +32,100 @@
3132
import org.springframework.context.ApplicationContextAware;
3233
import org.springframework.core.env.ConfigurableEnvironment;
3334
import org.springframework.core.env.Environment;
35+
import org.springframework.core.io.ByteArrayResource;
36+
import org.springframework.core.io.ClassPathResource;
37+
import org.springframework.core.io.FileSystemResource;
38+
import org.springframework.core.io.FileSystemResourceLoader;
39+
import org.springframework.core.io.FileUrlResource;
40+
import org.springframework.core.io.ProtocolResolver;
41+
import org.springframework.core.io.Resource;
42+
import org.springframework.core.io.ResourceLoader;
3443
import org.springframework.core.metrics.jfr.FlightRecorderApplicationStartup;
35-
import org.springframework.util.ObjectUtils;
3644

45+
import static java.nio.charset.StandardCharsets.UTF_8;
3746
import static org.assertj.core.api.Assertions.assertThat;
3847
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
3948
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
49+
import static org.assertj.core.api.InstanceOfAssertFactories.type;
4050
import static org.mockito.ArgumentMatchers.any;
4151
import static org.mockito.ArgumentMatchers.eq;
4252
import static org.mockito.Mockito.mock;
4353
import static org.mockito.Mockito.verify;
4454

4555
/**
56+
* Tests for {@link GenericApplicationContext}.
57+
*
4658
* @author Juergen Hoeller
4759
* @author Chris Beams
4860
* @author Stephane Nicoll
61+
* @author Sam Brannen
4962
*/
5063
class GenericApplicationContextTests {
5164

65+
private final GenericApplicationContext context = new GenericApplicationContext();
66+
67+
@AfterEach
68+
void closeContext() {
69+
context.close();
70+
}
71+
5272
@Test
5373
void getBeanForClass() {
54-
GenericApplicationContext ac = new GenericApplicationContext();
55-
ac.registerBeanDefinition("testBean", new RootBeanDefinition(String.class));
56-
ac.refresh();
74+
context.registerBeanDefinition("testBean", new RootBeanDefinition(String.class));
75+
context.refresh();
5776

58-
assertThat(ac.getBean("testBean")).isEqualTo("");
59-
assertThat(ac.getBean(String.class)).isSameAs(ac.getBean("testBean"));
60-
assertThat(ac.getBean(CharSequence.class)).isSameAs(ac.getBean("testBean"));
77+
assertThat(context.getBean("testBean")).isEqualTo("");
78+
assertThat(context.getBean(String.class)).isSameAs(context.getBean("testBean"));
79+
assertThat(context.getBean(CharSequence.class)).isSameAs(context.getBean("testBean"));
6180

62-
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(() ->
63-
ac.getBean(Object.class));
64-
ac.close();
81+
assertThatExceptionOfType(NoUniqueBeanDefinitionException.class)
82+
.isThrownBy(() -> context.getBean(Object.class));
6583
}
6684

6785
@Test
6886
void withSingletonSupplier() {
69-
GenericApplicationContext ac = new GenericApplicationContext();
70-
ac.registerBeanDefinition("testBean", new RootBeanDefinition(String.class, ac::toString));
71-
ac.refresh();
87+
context.registerBeanDefinition("testBean", new RootBeanDefinition(String.class, context::toString));
88+
context.refresh();
7289

73-
assertThat(ac.getBean("testBean")).isSameAs(ac.getBean("testBean"));
74-
assertThat(ac.getBean(String.class)).isSameAs(ac.getBean("testBean"));
75-
assertThat(ac.getBean(CharSequence.class)).isSameAs(ac.getBean("testBean"));
76-
assertThat(ac.getBean("testBean")).isEqualTo(ac.toString());
77-
ac.close();
90+
assertThat(context.getBean("testBean")).isSameAs(context.getBean("testBean"));
91+
assertThat(context.getBean(String.class)).isSameAs(context.getBean("testBean"));
92+
assertThat(context.getBean(CharSequence.class)).isSameAs(context.getBean("testBean"));
93+
assertThat(context.getBean("testBean")).isEqualTo(context.toString());
7894
}
7995

8096
@Test
8197
void withScopedSupplier() {
82-
GenericApplicationContext ac = new GenericApplicationContext();
83-
ac.registerBeanDefinition("testBean",
84-
new RootBeanDefinition(String.class, BeanDefinition.SCOPE_PROTOTYPE, ac::toString));
85-
ac.refresh();
98+
context.registerBeanDefinition("testBean",
99+
new RootBeanDefinition(String.class, BeanDefinition.SCOPE_PROTOTYPE, context::toString));
100+
context.refresh();
86101

87-
assertThat(ac.getBean("testBean")).isNotSameAs(ac.getBean("testBean"));
88-
assertThat(ac.getBean(String.class)).isEqualTo(ac.getBean("testBean"));
89-
assertThat(ac.getBean(CharSequence.class)).isEqualTo(ac.getBean("testBean"));
90-
assertThat(ac.getBean("testBean")).isEqualTo(ac.toString());
91-
ac.close();
102+
assertThat(context.getBean("testBean")).isNotSameAs(context.getBean("testBean"));
103+
assertThat(context.getBean(String.class)).isEqualTo(context.getBean("testBean"));
104+
assertThat(context.getBean(CharSequence.class)).isEqualTo(context.getBean("testBean"));
105+
assertThat(context.getBean("testBean")).isEqualTo(context.toString());
92106
}
93107

94108
@Test
95109
void accessAfterClosing() {
96-
GenericApplicationContext ac = new GenericApplicationContext();
97-
ac.registerBeanDefinition("testBean", new RootBeanDefinition(String.class));
98-
ac.refresh();
99-
100-
assertThat(ac.getBean(String.class)).isSameAs(ac.getBean("testBean"));
101-
assertThat(ac.getAutowireCapableBeanFactory().getBean(String.class)).isSameAs(ac.getAutowireCapableBeanFactory().getBean("testBean"));
110+
context.registerBeanDefinition("testBean", new RootBeanDefinition(String.class));
111+
context.refresh();
102112

103-
ac.close();
113+
assertThat(context.getBean(String.class)).isSameAs(context.getBean("testBean"));
114+
assertThat(context.getAutowireCapableBeanFactory().getBean(String.class))
115+
.isSameAs(context.getAutowireCapableBeanFactory().getBean("testBean"));
104116

105-
assertThatIllegalStateException().isThrownBy(() ->
106-
ac.getBean(String.class));
117+
context.close();
107118

108-
assertThatIllegalStateException().isThrownBy(() -> {
109-
ac.getAutowireCapableBeanFactory().getBean("testBean");
110-
ac.getAutowireCapableBeanFactory().getBean(String.class);
111-
});
112-
ac.close();
119+
assertThatIllegalStateException()
120+
.isThrownBy(() -> context.getBean(String.class));
121+
assertThatIllegalStateException()
122+
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean(String.class));
123+
assertThatIllegalStateException()
124+
.isThrownBy(() -> context.getAutowireCapableBeanFactory().getBean("testBean"));
113125
}
114126

115127
@Test
116128
void individualBeans() {
117-
GenericApplicationContext context = new GenericApplicationContext();
118129
context.registerBean(BeanA.class);
119130
context.registerBean(BeanB.class);
120131
context.registerBean(BeanC.class);
@@ -123,12 +134,10 @@ void individualBeans() {
123134
assertThat(context.getBean(BeanA.class).b).isSameAs(context.getBean(BeanB.class));
124135
assertThat(context.getBean(BeanA.class).c).isSameAs(context.getBean(BeanC.class));
125136
assertThat(context.getBean(BeanB.class).applicationContext).isSameAs(context);
126-
context.close();
127137
}
128138

129139
@Test
130140
void individualNamedBeans() {
131-
GenericApplicationContext context = new GenericApplicationContext();
132141
context.registerBean("a", BeanA.class);
133142
context.registerBean("b", BeanB.class);
134143
context.registerBean("c", BeanC.class);
@@ -137,12 +146,10 @@ void individualNamedBeans() {
137146
assertThat(context.getBean("a", BeanA.class).b).isSameAs(context.getBean("b"));
138147
assertThat(context.getBean("a", BeanA.class).c).isSameAs(context.getBean("c"));
139148
assertThat(context.getBean("b", BeanB.class).applicationContext).isSameAs(context);
140-
context.close();
141149
}
142150

143151
@Test
144152
void individualBeanWithSupplier() {
145-
GenericApplicationContext context = new GenericApplicationContext();
146153
context.registerBean(BeanA.class,
147154
() -> new BeanA(context.getBean(BeanB.class), context.getBean(BeanC.class)));
148155
context.registerBean(BeanB.class, BeanB::new);
@@ -154,14 +161,14 @@ void individualBeanWithSupplier() {
154161
assertThat(context.getBean(BeanA.class).c).isSameAs(context.getBean(BeanC.class));
155162
assertThat(context.getBean(BeanB.class).applicationContext).isSameAs(context);
156163

157-
assertThat(context.getDefaultListableBeanFactory().getDependentBeans(BeanB.class.getName())).isEqualTo(new String[] {BeanA.class.getName()});
158-
assertThat(context.getDefaultListableBeanFactory().getDependentBeans(BeanC.class.getName())).isEqualTo(new String[] {BeanA.class.getName()});
159-
context.close();
164+
assertThat(context.getDefaultListableBeanFactory().getDependentBeans(BeanB.class.getName()))
165+
.containsExactly(BeanA.class.getName());
166+
assertThat(context.getDefaultListableBeanFactory().getDependentBeans(BeanC.class.getName()))
167+
.containsExactly(BeanA.class.getName());
160168
}
161169

162170
@Test
163171
void individualBeanWithSupplierAndCustomizer() {
164-
GenericApplicationContext context = new GenericApplicationContext();
165172
context.registerBean(BeanA.class,
166173
() -> new BeanA(context.getBean(BeanB.class), context.getBean(BeanC.class)),
167174
bd -> bd.setLazyInit(true));
@@ -173,12 +180,10 @@ void individualBeanWithSupplierAndCustomizer() {
173180
assertThat(context.getBean(BeanA.class).b).isSameAs(context.getBean(BeanB.class));
174181
assertThat(context.getBean(BeanA.class).c).isSameAs(context.getBean(BeanC.class));
175182
assertThat(context.getBean(BeanB.class).applicationContext).isSameAs(context);
176-
context.close();
177183
}
178184

179185
@Test
180186
void individualNamedBeanWithSupplier() {
181-
GenericApplicationContext context = new GenericApplicationContext();
182187
context.registerBean("a", BeanA.class,
183188
() -> new BeanA(context.getBean(BeanB.class), context.getBean(BeanC.class)));
184189
context.registerBean("b", BeanB.class, BeanB::new);
@@ -189,12 +194,10 @@ void individualNamedBeanWithSupplier() {
189194
assertThat(context.getBean(BeanA.class).b).isSameAs(context.getBean("b", BeanB.class));
190195
assertThat(context.getBean("a", BeanA.class).c).isSameAs(context.getBean("c"));
191196
assertThat(context.getBean("b", BeanB.class).applicationContext).isSameAs(context);
192-
context.close();
193197
}
194198

195199
@Test
196200
void individualNamedBeanWithSupplierAndCustomizer() {
197-
GenericApplicationContext context = new GenericApplicationContext();
198201
context.registerBean("a", BeanA.class,
199202
() -> new BeanA(context.getBean(BeanB.class), context.getBean(BeanC.class)),
200203
bd -> bd.setLazyInit(true));
@@ -206,33 +209,63 @@ void individualNamedBeanWithSupplierAndCustomizer() {
206209
assertThat(context.getBean(BeanA.class).b).isSameAs(context.getBean("b", BeanB.class));
207210
assertThat(context.getBean("a", BeanA.class).c).isSameAs(context.getBean("c"));
208211
assertThat(context.getBean("b", BeanB.class).applicationContext).isSameAs(context);
209-
context.close();
210212
}
211213

212214
@Test
213215
void individualBeanWithNullReturningSupplier() {
214-
GenericApplicationContext context = new GenericApplicationContext();
215216
context.registerBean("a", BeanA.class, () -> null);
216217
context.registerBean("b", BeanB.class, BeanB::new);
217218
context.registerBean("c", BeanC.class, BeanC::new);
218219
context.refresh();
219220

220-
assertThat(ObjectUtils.containsElement(context.getBeanNamesForType(BeanA.class), "a")).isTrue();
221-
assertThat(ObjectUtils.containsElement(context.getBeanNamesForType(BeanB.class), "b")).isTrue();
222-
assertThat(ObjectUtils.containsElement(context.getBeanNamesForType(BeanC.class), "c")).isTrue();
223-
assertThat(context.getBeansOfType(BeanA.class).isEmpty()).isTrue();
224-
assertThat(context.getBeansOfType(BeanB.class).values().iterator().next()).isSameAs(context.getBean(BeanB.class));
225-
assertThat(context.getBeansOfType(BeanC.class).values().iterator().next()).isSameAs(context.getBean(BeanC.class));
226-
context.close();
221+
assertThat(context.getBeanNamesForType(BeanA.class)).containsExactly("a");
222+
assertThat(context.getBeanNamesForType(BeanB.class)).containsExactly("b");
223+
assertThat(context.getBeanNamesForType(BeanC.class)).containsExactly("c");
224+
assertThat(context.getBeansOfType(BeanA.class)).isEmpty();
225+
assertThat(context.getBeansOfType(BeanB.class).values().iterator().next())
226+
.isSameAs(context.getBean(BeanB.class));
227+
assertThat(context.getBeansOfType(BeanC.class).values().iterator().next())
228+
.isSameAs(context.getBean(BeanC.class));
227229
}
228230

229231
@Test
230232
void configureApplicationStartupOnBeanFactory() {
231233
FlightRecorderApplicationStartup applicationStartup = new FlightRecorderApplicationStartup();
232-
GenericApplicationContext context = new GenericApplicationContext();
233234
context.setApplicationStartup(applicationStartup);
234235
assertThat(context.getBeanFactory().getApplicationStartup()).isEqualTo(applicationStartup);
235-
context.close();
236+
}
237+
238+
@Test
239+
void getResourceWithDefaultResourceLoader() {
240+
assertGetResourceSemantics(null, ClassPathResource.class);
241+
}
242+
243+
@Test
244+
void getResourceWithCustomResourceLoader() {
245+
assertGetResourceSemantics(new FileSystemResourceLoader(), FileSystemResource.class);
246+
}
247+
248+
private void assertGetResourceSemantics(ResourceLoader resourceLoader, Class<? extends Resource> defaultResouceType) {
249+
if (resourceLoader != null) {
250+
context.setResourceLoader(resourceLoader);
251+
}
252+
253+
String pingLocation = "ping:foo";
254+
String fileLocation = "file:foo";
255+
256+
Resource resource = context.getResource(pingLocation);
257+
assertThat(resource).isInstanceOf(defaultResouceType);
258+
resource = context.getResource(fileLocation);
259+
assertThat(resource).isInstanceOf(FileUrlResource.class);
260+
261+
context.addProtocolResolver(new PingPongProtocolResolver());
262+
263+
resource = context.getResource(pingLocation);
264+
assertThat(resource).asInstanceOf(type(ByteArrayResource.class))
265+
.extracting(bar -> new String(bar.getByteArray(), UTF_8))
266+
.isEqualTo("pong:foo");
267+
resource = context.getResource(fileLocation);
268+
assertThat(resource).isInstanceOf(FileUrlResource.class);
236269
}
237270

238271
@Test
@@ -455,4 +488,15 @@ protected T createInstance() {
455488
}
456489
}
457490

491+
static class PingPongProtocolResolver implements ProtocolResolver {
492+
493+
@Override
494+
public Resource resolve(String location, ResourceLoader resourceLoader) {
495+
if (location.startsWith("ping:")) {
496+
return new ByteArrayResource(("pong:" + location.substring(5)).getBytes(UTF_8));
497+
}
498+
return null;
499+
}
500+
}
501+
458502
}

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)