Skip to content

Inject the class loader from the parent context into the child context #1098

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.core.env.MapPropertySource;
Expand All @@ -45,6 +47,7 @@
* @param <C> specification
* @author Spencer Gibb
* @author Dave Syer
* @author Tommy Karlsson
*/
// TODO: add javadoc
public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
Expand Down Expand Up @@ -110,7 +113,25 @@ protected AnnotationConfigApplicationContext getContext(String name) {
}

protected AnnotationConfigApplicationContext createContext(String name) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
AnnotationConfigApplicationContext context;
if (this.parent != null) {
// jdk11 issue
// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
// https://github.com/spring-cloud/spring-cloud-openfeign/issues/475
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
if (parent instanceof ConfigurableApplicationContext) {
beanFactory.setBeanClassLoader(
((ConfigurableApplicationContext) parent).getBeanFactory().getBeanClassLoader());
}
else {
beanFactory.setBeanClassLoader(parent.getClassLoader());
}
context = new AnnotationConfigApplicationContext(beanFactory);
context.setClassLoader(this.parent.getClassLoader());
}
else {
context = new AnnotationConfigApplicationContext();
}
if (this.configurations.containsKey(name)) {
for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {
context.register(configuration);
Expand All @@ -129,9 +150,6 @@ protected AnnotationConfigApplicationContext createContext(String name) {
if (this.parent != null) {
// Uses Environment from parent as well as beans
context.setParent(this.parent);
// jdk11 issue
// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
context.setClassLoader(this.parent.getClassLoader());
}
context.setDisplayName(generateDisplayName(name));
context.refresh();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,26 @@

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.util.ClassUtils;

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

/**
* @author Spencer Gibb
* @author Tommy Karlsson
*/
public class NamedContextFactoryTests {

Expand All @@ -37,6 +46,10 @@ public void testChildContexts() {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
parent.register(BaseConfig.class);
parent.refresh();
testChildContexts(parent);
}

private void testChildContexts(GenericApplicationContext parent) {
TestClientFactory factory = new TestClientFactory();
factory.setApplicationContext(parent);
factory.setConfigurations(Arrays.asList(getSpec("foo", FooConfig.class), getSpec("bar", BarConfig.class)));
Expand Down Expand Up @@ -79,6 +92,10 @@ public void testChildContexts() {
then(fooContext.getClassLoader()).as("foo context classloader does not match parent")
.isSameAs(parent.getClassLoader());

then(fooContext.getBeanFactory().getBeanClassLoader())
.as("foo context bean factory classloader does not match parent")
.isSameAs(parent.getBeanFactory().getBeanClassLoader());

Assertions.assertThat(fooContext).hasFieldOrPropertyWithValue("customClassLoader", true);

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

@Test
void testBadThreadContextClassLoader() throws InterruptedException, ExecutionException, TimeoutException {
AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();
parent.setClassLoader(ClassUtils.getDefaultClassLoader());
parent.register(BaseConfig.class);
parent.refresh();

ExecutorService es = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r);
t.setContextClassLoader(new ThrowingClassLoader());
return t;
});

es.submit(() -> this.testChildContexts(parent)).get(5, TimeUnit.SECONDS);
}

private TestSpec getSpec(String name, Class<?> configClass) {
return new TestSpec(name, new Class[] { configClass });
}

static class ThrowingClassLoader extends ClassLoader {

ThrowingClassLoader() {
super(null);
}

@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}

}

static class TestClientFactory extends NamedContextFactory<TestSpec> {

TestClientFactory() {
Expand Down Expand Up @@ -147,6 +193,7 @@ static class Baz {

}

@ConditionalOnClass(Object.class)
static class FooConfig {

@Bean
Expand Down