Why Does ApplicationContextInitializer Run Twice? Debugging Spring Boot Startup
This article explains the purpose of ApplicationContextInitializer in Spring Boot, shows how to implement custom conditions and initializers for environment‑specific configuration, and investigates why the initializer may execute twice when Spring Cloud Context is added, leading to bean duplication and port conflicts.
ApplicationContextInitializer Introduction
The initializer works on a
ConfigurableApplicationContext(an
ApplicationContext) and allows us to enhance the context before the
refreshoperation.
Business Scenario
In real‑world web applications we often need to programmatically initialize the application context, such as registering property sources (bootstrap/application properties) and dynamically activating different profiles based on the environment. For example, a recent project required loading different SDK parameters depending on whether the OS is Linux or Windows.
Custom Condition Implementation
Because of the large number of configuration items, we can customize
@Conditionalon a
ConfigurationPropertiesbean.
<code>public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String property = context.getEnvironment().getProperty("os.name");
// ...
return property.contains("linux");
}
}
</code> <code>@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({LinuxCondition.class})
public @interface ConditionOnLinux {}
</code>Due to the SDK’s overly complex configuration classes, this approach was eventually abandoned.
Custom ApplicationContextInitializer Implementation
Based on the context environment, we load resource directories and configuration files for different environments.
<code>public class SelectApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext context) {
ConfigurableEnvironment env = context.getEnvironment();
MutablePropertySources mps = env.getPropertySources();
String property = env.getProperty("os.name");
if (property.contains("Mac OS X")) {
mps.addLast(new ResourcePropertySource(new ClassPathResource("linux.properties")));
} else {
mps.addLast(new ResourcePropertySource(new ClassPathResource("window.properties")));
}
}
}
</code>Problem: initialize Executes Twice
When the functionality is extracted into a starter and runs in a monolithic Spring Boot project, adding
Spring Cloud Contextcauses the initializer to run twice.
SpringApplication.run
To locate the root cause, a breakpoint was set in
SpringApplication.run.
BootstrapApplicationListener
The investigation reached the
BootstrapApplicationListener.bootstrapServiceContextmethod.
We examined the
bootstrapServiceContextmethod.
<code>SpringApplicationBuilder builder = (new SpringApplicationBuilder(new Class[0]))
.profiles(environment.getActiveProfiles())
.bannerMode(Mode.OFF)
.environment(bootstrapEnvironment)
.registerShutdownHook(false)
.logStartupInfo(false)
.web(WebApplicationType.NONE);
SpringApplication builderApplication = builder.application();
if (builderApplication.getMainApplicationClass() == null) {
builder.main(application.getMainApplicationClass());
}
if (environment.getPropertySources().contains("refreshArgs")) {
builderApplication.setListeners(this.filterListeners(builderApplication.getListeners()));
}
builder.sources(BootstrapImportSelectorConfiguration.class);
ConfigurableApplicationContext context = builder.run(new String[0]);
context.setId("bootstrap");
</code>Truth Warning
Inside
BootstrapApplicationListener,
SpringApplicationBuildertriggers a restart; although
Runis called twice, the first run does not start the container, leading to beans being loaded twice or port conflicts such as Tomcat.
Java Architecture Diary
Committed to sharing original, high‑quality technical articles; no fluff or promotional content.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.