Skip to content

Commit e293e69

Browse files
authored
Inject the class loader from the parent context into the child context (#1098)
1 parent 353d4f3 commit e293e69

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

spring-cloud-context/src/main/java/org/springframework/cloud/context/named/NamedContextFactory.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@
2929
import org.springframework.beans.factory.DisposableBean;
3030
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
3131
import org.springframework.beans.factory.ObjectProvider;
32+
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
3233
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
3334
import org.springframework.context.ApplicationContext;
3435
import org.springframework.context.ApplicationContextAware;
36+
import org.springframework.context.ConfigurableApplicationContext;
3537
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
3638
import org.springframework.core.ResolvableType;
3739
import org.springframework.core.env.MapPropertySource;
@@ -45,6 +47,7 @@
4547
* @param <C> specification
4648
* @author Spencer Gibb
4749
* @author Dave Syer
50+
* @author Tommy Karlsson
4851
*/
4952
// TODO: add javadoc
5053
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
@@ -110,7 +113,25 @@ protected AnnotationConfigApplicationContext getContext(String name) {
110113
}
111114

112115
protected AnnotationConfigApplicationContext createContext(String name) {
113-
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
116+
AnnotationConfigApplicationContext context;
117+
if (this.parent != null) {
118+
// jdk11 issue
119+
// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
120+
// https://github.com/spring-cloud/spring-cloud-openfeign/issues/475
121+
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
122+
if (parent instanceof ConfigurableApplicationContext) {
123+
beanFactory.setBeanClassLoader(
124+
((ConfigurableApplicationContext) parent).getBeanFactory().getBeanClassLoader());
125+
}
126+
else {
127+
beanFactory.setBeanClassLoader(parent.getClassLoader());
128+
}
129+
context = new AnnotationConfigApplicationContext(beanFactory);
130+
context.setClassLoader(this.parent.getClassLoader());
131+
}
132+
else {
133+
context = new AnnotationConfigApplicationContext();
134+
}
114135
if (this.configurations.containsKey(name)) {
115136
for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {
116137
context.register(configuration);
@@ -129,9 +150,6 @@ protected AnnotationConfigApplicationContext createContext(String name) {
129150
if (this.parent != null) {
130151
// Uses Environment from parent as well as beans
131152
context.setParent(this.parent);
132-
// jdk11 issue
133-
// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
134-
context.setClassLoader(this.parent.getClassLoader());
135153
}
136154
context.setDisplayName(generateDisplayName(name));
137155
context.refresh();

spring-cloud-context/src/test/java/org/springframework/cloud/context/named/NamedContextFactoryTests.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,26 @@
1818

1919
import java.util.Arrays;
2020
import java.util.Map;
21+
import java.util.concurrent.ExecutionException;
22+
import java.util.concurrent.ExecutorService;
23+
import java.util.concurrent.Executors;
24+
import java.util.concurrent.TimeUnit;
25+
import java.util.concurrent.TimeoutException;
2126

2227
import org.assertj.core.api.Assertions;
2328
import org.junit.jupiter.api.Test;
2429

30+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2531
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
2632
import org.springframework.context.annotation.Bean;
33+
import org.springframework.context.support.GenericApplicationContext;
34+
import org.springframework.util.ClassUtils;
2735

2836
import static org.assertj.core.api.BDDAssertions.then;
2937

3038
/**
3139
* @author Spencer Gibb
40+
* @author Tommy Karlsson
3241
*/
3342
public class NamedContextFactoryTests {
3443

@@ -37,6 +46,10 @@ public void testChildContexts() {
3746
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
3847
parent.register(BaseConfig.class);
3948
parent.refresh();
49+
testChildContexts(parent);
50+
}
51+
52+
private void testChildContexts(GenericApplicationContext parent) {
4053
TestClientFactory factory = new TestClientFactory();
4154
factory.setApplicationContext(parent);
4255
factory.setConfigurations(Arrays.asList(getSpec("foo", FooConfig.class), getSpec("bar", BarConfig.class)));
@@ -79,6 +92,10 @@ public void testChildContexts() {
7992
then(fooContext.getClassLoader()).as("foo context classloader does not match parent")
8093
.isSameAs(parent.getClassLoader());
8194

95+
then(fooContext.getBeanFactory().getBeanClassLoader())
96+
.as("foo context bean factory classloader does not match parent")
97+
.isSameAs(parent.getBeanFactory().getBeanClassLoader());
98+
8299
Assertions.assertThat(fooContext).hasFieldOrPropertyWithValue("customClassLoader", true);
83100

84101
factory.destroy();
@@ -88,10 +105,39 @@ public void testChildContexts() {
88105
then(barContext.isActive()).as("bar context wasn't closed").isFalse();
89106
}
90107

108+
@Test
109+
void testBadThreadContextClassLoader() throws InterruptedException, ExecutionException, TimeoutException {
110+
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
111+
parent.setClassLoader(ClassUtils.getDefaultClassLoader());
112+
parent.register(BaseConfig.class);
113+
parent.refresh();
114+
115+
ExecutorService es = Executors.newSingleThreadExecutor(r -> {
116+
Thread t = new Thread(r);
117+
t.setContextClassLoader(new ThrowingClassLoader());
118+
return t;
119+
});
120+
121+
es.submit(() -> this.testChildContexts(parent)).get(5, TimeUnit.SECONDS);
122+
}
123+
91124
private TestSpec getSpec(String name, Class<?> configClass) {
92125
return new TestSpec(name, new Class[] { configClass });
93126
}
94127

128+
static class ThrowingClassLoader extends ClassLoader {
129+
130+
ThrowingClassLoader() {
131+
super(null);
132+
}
133+
134+
@Override
135+
public Class<?> loadClass(String name) throws ClassNotFoundException {
136+
throw new ClassNotFoundException(name);
137+
}
138+
139+
}
140+
95141
static class TestClientFactory extends NamedContextFactory<TestSpec> {
96142

97143
TestClientFactory() {
@@ -147,6 +193,7 @@ static class Baz {
147193

148194
}
149195

196+
@ConditionalOnClass(Object.class)
150197
static class FooConfig {
151198

152199
@Bean

0 commit comments

Comments
 (0)