Affects: 5.2.9
It seems that if a dependent thread-scoped bean is retrieved from the ApplicationContext before the thread-scoped dependency, then bean creation could lock up in an infinite loop, never to return again.
Regression bug. Works fine with spring-framework 5.2.7, fails with spring-framework 5.2.9. May be a bug in spring-test rather than spring-context. May be hard to reproduce, hoping that this limited information is sufficient to lead to a fix.
SimpleThreadScope likely relevant since SimpleThreadScope has an infinite loop - the bean creation freezes in:
// org.springframework.context.support.SimpleThreadScope.class, the scope map is empty
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = (Map)this.threadScope.get();
return scope.computeIfAbsent(name, (k) -> {
return objectFactory.getObject();
});
Standalone test case:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.SimpleThreadScope;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes={MyTest.MyTestConfig.class},
loader= AnnotationConfigContextLoader.class)
@TestExecutionListeners({MyTest.MyTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class})
public class MyTest {
@Autowired
private MyView removeNodeStatusScreen;
@Test
void justRun() {
System.out.println("This test should run");
}
@Configuration
static class MyTestConfig {
@Bean
@org.springframework.context.annotation.Scope("thread")
// The bean name seems to matter, if I change the name the test passes
public MyView removeNodeStatusScreen(
MyPresenter myPresenter) {
return new MyView(myPresenter);
}
@Bean
@org.springframework.context.annotation.Scope("thread")
// The bean name seems to matter, if I change the name the test passes
public MyPresenter removeNodeStatusPresenter() {
return new MyPresenter();
}
}
static class MyTestExecutionListener
extends AbstractTestExecutionListener {
@Override
public void prepareTestInstance(TestContext testContext) throws Exception {
if (testContext.getApplicationContext() instanceof GenericApplicationContext) {
GenericApplicationContext context =
(GenericApplicationContext) testContext.getApplicationContext();
ConfigurableListableBeanFactory beanFactory = context
.getBeanFactory();
Scope viewScope = new SimpleThreadScope();
beanFactory.registerScope("thread", viewScope);
}
}
}
public static class MyPresenter {
public MyPresenter() {
}
}
public static class MyView {
public MyView(MyPresenter myPresenter) {
}
}
}
2 possible workarounds found:
Workaround 1: Add @DependsOn annotation to configuration class. However, @DependsOn would not be necessary if there were no bug:
@Configuration
public class MyConfig {
@ViewScope
@Bean
@DependsOn("myPresenter")
public MyView myView(MyPresenter myPresenter) {
return new MyView(myPresenter);
}
@Bean
@ViewScope
public MyPresenter myPresenter() {
return new myPresenter();
}
}
Workaround 2: swap order of beans in test. The order of the beans would not matter if there were no bug.
class MyConfigTest {
// myView depends on myPresenter, so make sure the myPresenter is declared first
// Otherwise test freezes on bean creation
@Autowired
MyPresenter myPresenter;
@Autowired
MyView myView;
...
Affects: 5.2.9
It seems that if a dependent thread-scoped bean is retrieved from the
ApplicationContextbefore the thread-scoped dependency, then bean creation could lock up in an infinite loop, never to return again.Regression bug. Works fine with spring-framework 5.2.7, fails with spring-framework 5.2.9. May be a bug in spring-test rather than spring-context. May be hard to reproduce, hoping that this limited information is sufficient to lead to a fix.
SimpleThreadScopelikely relevant sinceSimpleThreadScopehas an infinite loop - the bean creation freezes in:Standalone test case:
2 possible workarounds found:
Workaround 1: Add
@DependsOnannotation to configuration class. However,@DependsOnwould not be necessary if there were no bug:Workaround 2: swap order of beans in test. The order of the beans would not matter if there were no bug.