After https://github.com/spring-projects/spring-boot/pull/37905 fix, child context created with SpringApplicationBuilder runs parents runners. Child context creation code:
SpringApplication springApplication = new SpringApplicationBuilder(childMainClass)
.parent((ConfigurableApplicationContext) applicationContext)
.bannerMode(Banner.Mode.OFF)
.build();
ConfigurableApplicationContext context = springApplication.run();
Code above is executed from parents CommandLineRunner and leads to infinite context recursion. Parents runner creates child context, which invokes parents runner and so on.
Looks like problem is in using context.getBeanProvider(Runner.class) instead of context.getBeansOfType(ApplicationRunner.class) in SpringApplication::callRunners
Comment From: wilkinsona
Thanks for the report. Unfortunately, I'm not sure that I've understood the arrangement that you have described. Are you using the SpringApplicationBuilder API in a CommandLineRunner implementation? To clarify, please provide a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.
Comment From: fgsfb
Yes, I'm trying to create child context in parents runner implementation. And it worked well on previous versions. reproduce.zip
Comment From: wilkinsona
Thanks very much. I understand things now.
You can work around the problem by only allowing the parent runner to run once:
package com.reproduce.parent;
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.beans.BeansException;
import org.springframework.boot.Banner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
import com.reproduce.child.ChildMainClass;
@Component
public class ParentRunner implements CommandLineRunner, ApplicationContextAware {
private final AtomicBoolean run = new AtomicBoolean();
private ApplicationContext applicationContext;
@Override
public void run(String... args) throws Exception {
if (run.compareAndSet(false, true)) {
System.out.println("Parent runner");
new SpringApplicationBuilder(ChildMainClass.class)
.parent((ConfigurableApplicationContext) applicationContext)
.bannerMode(Banner.Mode.OFF)
.run();
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}