or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

admin-jmx.mdansi-support.mdaot-native-image.mdapplication-info.mdavailability.mdbootstrap.mdbootstrapping.mdbuilder.mdcloud-platform.mdconfiguration-annotations.mdconfiguration-data.mdconfiguration-properties.mdconversion.mddiagnostics.mdenvironment-property-sources.mdindex.mdjson-support.mdlifecycle-events.mdlogging.mdorigin-tracking.mdresource-loading.mdretry-support.mdssl-tls.mdstartup-metrics.mdsupport.mdsystem-utilities.mdtask-execution.mdthreading.mdutilities.mdvalidation.mdweb-support.md
tile.json

builder.mddocs/

SpringApplicationBuilder

Package: org.springframework.boot.builder Primary Class: SpringApplicationBuilder Since: 1.0.0

Fluent API builder for SpringApplication and ApplicationContext instances with support for context hierarchies. Provides method chaining for convenient configuration of Spring Boot applications.

Overview

SpringApplicationBuilder provides a fluent builder API for creating and configuring SpringApplication instances. Unlike SpringApplication's setter methods (which return void), all SpringApplicationBuilder methods return the builder instance, enabling method chaining.

Key Features:

  • Fluent method chaining for all SpringApplication configuration options
  • Context hierarchy support (parent/child/sibling contexts)
  • Cleaner, more readable application configuration code
  • Simplified creation of multi-context applications

Quick Reference

Basic vs Builder API

// SpringApplication - requires multiple statements
SpringApplication app = new SpringApplication(MyApp.class);
app.setWebApplicationType(WebApplicationType.SERVLET);
app.setBannerMode(Banner.Mode.OFF);
app.setLogStartupInfo(false);
app.run(args);

// SpringApplicationBuilder - fluent chaining
new SpringApplicationBuilder(MyApp.class)
    .web(WebApplicationType.SERVLET)
    .bannerMode(Banner.Mode.OFF)
    .logStartupInfo(false)
    .run(args);

Context Hierarchy

// Parent-child context hierarchy
new SpringApplicationBuilder(ParentConfig.class)
    .child(ChildConfig.class)
    .run(args);

// Sibling contexts
new SpringApplicationBuilder(MainConfig.class)
    .sibling(SiblingConfig.class)
    .run(args);

Core API

SpringApplicationBuilder Class

package org.springframework.boot.builder;

import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.bootstrap.BootstrapRegistryInitializer;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.metrics.ApplicationStartup;
import java.util.Map;
import java.util.Properties;

/**
 * Builder for SpringApplication with fluent API and context hierarchy support.
 * @since 1.0.0
 */
public class SpringApplicationBuilder {

    // Constructors
    public SpringApplicationBuilder(Class<?>... sources);
    public SpringApplicationBuilder(ResourceLoader resourceLoader, Class<?>... sources);

    // Build and run
    public ConfigurableApplicationContext run(String... args);
    public SpringApplication build();
    public SpringApplication build(String... args);

    // Access current state
    public SpringApplication application();
    public ConfigurableApplicationContext context();

    // Context hierarchy
    public SpringApplicationBuilder child(Class<?>... sources);
    public SpringApplicationBuilder parent(Class<?>... sources);
    public SpringApplicationBuilder parent(ConfigurableApplicationContext parent);
    public SpringApplicationBuilder sibling(Class<?>... sources);
    public SpringApplicationBuilder sibling(Class<?>[] sources, String... args);

    // Configuration sources
    public SpringApplicationBuilder sources(Class<?>... sources);

    // Application type
    public SpringApplicationBuilder web(WebApplicationType webApplicationType);

    // Logging and banner
    public SpringApplicationBuilder logStartupInfo(boolean logStartupInfo);
    public SpringApplicationBuilder banner(Banner banner);
    public SpringApplicationBuilder bannerMode(Banner.Mode bannerMode);

    // System configuration
    public SpringApplicationBuilder headless(boolean headless);
    public SpringApplicationBuilder registerShutdownHook(boolean registerShutdownHook);
    public SpringApplicationBuilder main(Class<?> mainApplicationClass);

    // Properties and profiles
    public SpringApplicationBuilder addCommandLineProperties(boolean addCommandLineProperties);
    public SpringApplicationBuilder setAddConversionService(boolean addConversionService);
    public SpringApplicationBuilder properties(String... defaultProperties);
    public SpringApplicationBuilder properties(Properties defaultProperties);
    public SpringApplicationBuilder properties(Map<String, Object> defaults);
    public SpringApplicationBuilder profiles(String... profiles);

    // Bean configuration
    public SpringApplicationBuilder beanNameGenerator(BeanNameGenerator beanNameGenerator);
    public SpringApplicationBuilder lazyInitialization(boolean lazyInitialization);
    public SpringApplicationBuilder allowCircularReferences(boolean allowCircularReferences);

    // Environment and resources
    public SpringApplicationBuilder environment(ConfigurableEnvironment environment);
    public SpringApplicationBuilder environmentPrefix(String environmentPrefix);
    public SpringApplicationBuilder resourceLoader(ResourceLoader resourceLoader);

    // Customization hooks
    public SpringApplicationBuilder contextFactory(ApplicationContextFactory factory);
    public SpringApplicationBuilder initializers(ApplicationContextInitializer<?>... initializers);
    public SpringApplicationBuilder listeners(ApplicationListener<?>... listeners);
    public SpringApplicationBuilder addBootstrapRegistryInitializer(BootstrapRegistryInitializer initializer);
    public SpringApplicationBuilder applicationStartup(ApplicationStartup applicationStartup);

    // Protected methods for subclassing
    protected SpringApplication createSpringApplication(ResourceLoader resourceLoader, Class<?>... sources);
}

Configuration Methods

Basic Configuration

import org.springframework.boot.Banner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;

public class MyApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(MyApplication.class)
            // Set web application type
            .web(WebApplicationType.SERVLET)  // SERVLET, REACTIVE, or NONE

            // Configure banner
            .bannerMode(Banner.Mode.OFF)  // OFF, CONSOLE, or LOG

            // Logging
            .logStartupInfo(true)

            // Headless mode
            .headless(true)

            // Register shutdown hook
            .registerShutdownHook(true)

            // Run
            .run(args);
    }
}

Properties and Profiles

import org.springframework.boot.builder.SpringApplicationBuilder;

public class MyApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(MyApplication.class)
            // Set default properties (string format)
            .properties(
                "server.port=8080",
                "spring.application.name=my-app",
                "logging.level.root=INFO"
            )

            // Set active profiles
            .profiles("dev", "local")

            // Add command-line properties
            .addCommandLineProperties(true)

            .run(args);
    }
}

Alternative property formats:

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

// Using Map
Map<String, Object> props = new HashMap<>();
props.put("server.port", 8080);
props.put("spring.application.name", "my-app");

new SpringApplicationBuilder(MyApplication.class)
    .properties(props)
    .run(args);

// Using Properties
Properties props = new Properties();
props.setProperty("server.port", "8080");
props.setProperty("spring.application.name", "my-app");

new SpringApplicationBuilder(MyApplication.class)
    .properties(props)
    .run(args);

Context Hierarchy

Create parent-child context relationships for modular applications:

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// Parent context configuration
@Configuration
class ParentConfig {
    @Bean
    public SharedService sharedService() {
        return new SharedService();
    }
}

// Child context configuration
@SpringBootApplication
class ChildConfig {
    // Can access beans from parent context
}

public class HierarchicalApplication {

    public static void main(String[] args) {
        // Create parent-child hierarchy
        new SpringApplicationBuilder(ParentConfig.class)
            .web(WebApplicationType.NONE)  // Parent typically non-web
            .child(ChildConfig.class)
            .web(WebApplicationType.SERVLET)  // Child handles web requests
            .run(args);
    }
}

Multiple children:

// Parent with two child contexts
SpringApplicationBuilder parent = new SpringApplicationBuilder(ParentConfig.class)
    .web(WebApplicationType.NONE);

parent.child(WebConfig.class)
    .web(WebApplicationType.SERVLET)
    .properties("server.port=8080")
    .run(args);

parent.child(AdminConfig.class)
    .web(WebApplicationType.SERVLET)
    .properties("server.port=8081")
    .run(args);

Sibling Contexts

Create independent sibling contexts:

import org.springframework.boot.builder.SpringApplicationBuilder;

public class SiblingApplication {

    public static void main(String[] args) {
        // Main application
        SpringApplicationBuilder main = new SpringApplicationBuilder(MainConfig.class)
            .web(WebApplicationType.SERVLET)
            .properties("server.port=8080");

        // Create sibling context (independent)
        main.sibling(AdminConfig.class)
            .web(WebApplicationType.SERVLET)
            .properties("server.port=9090")
            .run(args);

        // Run main context
        main.run(args);
    }
}

Custom Initialization

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.bootstrap.BootstrapRegistryInitializer;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.boot.context.event.ApplicationReadyEvent;

public class CustomizedApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(MyApplication.class)
            // Bootstrap registry initializers
            .addBootstrapRegistryInitializer(registry -> {
                registry.register(MyService.class,
                    context -> new MyService());
            })

            // Application context initializers
            .initializers(context -> {
                System.out.println("Initializing context: " + context.getId());
            })

            // Event listeners
            .listeners((ApplicationListener<ApplicationReadyEvent>) event -> {
                System.out.println("Application ready!");
            })

            .run(args);
    }
}

Advanced Configuration

import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;

public class AdvancedApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(MyApplication.class)
            // Lazy initialization
            .lazyInitialization(true)

            // Allow circular references
            .allowCircularReferences(false)

            // Custom environment
            .environment(new StandardEnvironment())

            // Environment prefix
            .environmentPrefix("myapp")

            // Custom bean name generator
            .beanNameGenerator((definition, registry) ->
                "custom-" + definition.getBeanClassName())

            // Application startup metrics
            .applicationStartup(new BufferingApplicationStartup(2048))

            .run(args);
    }
}

Common Patterns

Pattern 1: Production Application with Full Configuration

Complete production-ready application configuration:

import org.springframework.boot.Banner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;

public class ProductionApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(ProductionApplication.class)
            // Application type
            .web(WebApplicationType.SERVLET)

            // Profiles
            .profiles("prod")

            // Properties
            .properties(
                "spring.application.name=my-service",
                "server.port=8080",
                "management.endpoints.web.exposure.include=health,metrics",
                "logging.level.root=INFO"
            )

            // Logging
            .logStartupInfo(true)
            .bannerMode(Banner.Mode.LOG)

            // Lifecycle
            .registerShutdownHook(true)

            // Performance
            .lazyInitialization(false)
            .applicationStartup(new BufferingApplicationStartup(4096))

            // Security
            .allowCircularReferences(false)

            // Listeners
            .listeners(event -> {
                if (event instanceof org.springframework.boot.context.event.ApplicationFailedEvent) {
                    System.err.println("Application failed: " + event);
                }
            })

            .run(args);
    }
}

Pattern 2: Multi-Module Microservices Architecture

Parent context with shared services and multiple child web contexts:

import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
class SharedInfrastructure {
    @Bean
    public DataSource dataSource() {
        return new DataSource();
    }

    @Bean
    public CacheManager cacheManager() {
        return new CacheManager();
    }
}

@SpringBootApplication
class PublicApiService {
    // Public-facing API
}

@SpringBootApplication
class AdminApiService {
    // Admin API
}

public class MicroservicesPlatform {

    public static void main(String[] args) {
        // Shared infrastructure context
        SpringApplicationBuilder parent = new SpringApplicationBuilder(SharedInfrastructure.class)
            .web(WebApplicationType.NONE)
            .properties(
                "spring.datasource.url=jdbc:postgresql://localhost:5432/mydb"
            );

        // Public API service on port 8080
        parent.child(PublicApiService.class)
            .web(WebApplicationType.SERVLET)
            .properties(
                "server.port=8080",
                "spring.application.name=public-api"
            )
            .profiles("public")
            .run(args);

        // Admin API service on port 9090
        parent.child(AdminApiService.class)
            .web(WebApplicationType.SERVLET)
            .properties(
                "server.port=9090",
                "spring.application.name=admin-api"
            )
            .profiles("admin")
            .run(args);
    }
}

Pattern 3: Environment-Specific Configuration

Configure application differently based on environment:

import org.springframework.boot.Banner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;

public class EnvironmentAwareApplication {

    public static void main(String[] args) {
        String environment = System.getenv("APP_ENV");
        boolean isProduction = "production".equals(environment);

        SpringApplicationBuilder builder = new SpringApplicationBuilder(MyApplication.class)
            .web(WebApplicationType.SERVLET);

        if (isProduction) {
            // Production configuration
            builder
                .profiles("prod")
                .bannerMode(Banner.Mode.OFF)
                .logStartupInfo(true)
                .lazyInitialization(false)
                .allowCircularReferences(false)
                .properties(
                    "logging.level.root=WARN",
                    "server.tomcat.threads.max=200"
                );
        } else {
            // Development configuration
            builder
                .profiles("dev")
                .bannerMode(Banner.Mode.CONSOLE)
                .logStartupInfo(true)
                .lazyInitialization(true)
                .properties(
                    "logging.level.root=DEBUG",
                    "spring.devtools.restart.enabled=true"
                );
        }

        builder.run(args);
    }
}

Pattern 4: Testing Configuration

Build applications for integration tests:

import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.junit.jupiter.api.Test;

public class IntegrationTest {

    @Test
    void testApplication() {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(TestConfig.class)
            .web(WebApplicationType.NONE)
            .profiles("test")
            .properties(
                "spring.datasource.url=jdbc:h2:mem:testdb",
                "logging.level.root=DEBUG"
            )
            .registerShutdownHook(false)  // Manually control lifecycle
            .run();

        try {
            // Perform tests
            MyService service = context.getBean(MyService.class);
            // Test service
        } finally {
            context.close();
        }
    }
}

Thread Safety

  • SpringApplicationBuilder: Not thread-safe, use from single thread
  • Built SpringApplication: Not thread-safe for configuration, but can be run multiple times
  • Context Hierarchy: Each context has independent lifecycle and thread safety

Best Practices

  1. Use Builder for Chaining: Prefer SpringApplicationBuilder over SpringApplication when you need multiple configuration calls
  2. Parent Context for Shared Resources: Put shared services in parent context to avoid duplication
  3. Non-Web Parents: Parent contexts are typically non-web (WebApplicationType.NONE)
  4. Profile Separation: Use different profiles for parent and child contexts
  5. Port Configuration: Always configure different ports for sibling web contexts
  6. Build Once, Run Once: Don't reuse builder instances for multiple applications
  7. Document Hierarchies: Clearly document context hierarchy relationships

Common Pitfalls

Pitfall 1: Reusing Builder Instance

Problem: Attempting to reuse builder for multiple applications.

Solution:

// WRONG - builder state is reused
SpringApplicationBuilder builder = new SpringApplicationBuilder(MyApp.class);
builder.web(WebApplicationType.SERVLET).run(args);
builder.web(WebApplicationType.REACTIVE).run(args);  // Unexpected behavior

// CORRECT - create new builder for each application
new SpringApplicationBuilder(ServletApp.class)
    .web(WebApplicationType.SERVLET)
    .run(args);

new SpringApplicationBuilder(ReactiveApp.class)
    .web(WebApplicationType.REACTIVE)
    .run(args);

Pitfall 2: Web-Type Parent Context

Problem: Making parent context a web application causes port conflicts.

Solution:

// WRONG - parent context tries to start web server
new SpringApplicationBuilder(ParentConfig.class)
    .web(WebApplicationType.SERVLET)  // Will try to bind to port!
    .child(ChildConfig.class)
    .run(args);

// CORRECT - parent should be non-web
new SpringApplicationBuilder(ParentConfig.class)
    .web(WebApplicationType.NONE)  // No web server
    .child(ChildConfig.class)
    .web(WebApplicationType.SERVLET)  // Child handles web
    .run(args);

Pitfall 3: Forgetting Port Configuration for Siblings

Problem: Sibling contexts both try to bind to default port 8080.

Solution:

// WRONG - both contexts use port 8080
main.sibling(AdminConfig.class)
    .web(WebApplicationType.SERVLET)
    .run(args);  // Port conflict!

// CORRECT - configure different ports
main.sibling(AdminConfig.class)
    .web(WebApplicationType.SERVLET)
    .properties("server.port=9090")
    .run(args);

Pitfall 4: Expecting Property Inheritance

Problem: Assuming child contexts inherit all parent properties.

Solution:

// Properties don't automatically inherit to children
SpringApplicationBuilder parent = new SpringApplicationBuilder(ParentConfig.class)
    .properties("my.property=value");  // NOT inherited

// Explicitly set properties on child if needed
parent.child(ChildConfig.class)
    .properties("my.property=value")  // Must explicitly set
    .run(args);

Related Classes

ParentContextApplicationContextInitializer

package org.springframework.boot.builder;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;

/**
 * ApplicationContextInitializer for setting parent context.
 * @since 1.0.0
 */
public class ParentContextApplicationContextInitializer
        implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    public ParentContextApplicationContextInitializer(ConfigurableApplicationContext parent);

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext);

    /**
     * Event published when parent context becomes available.
     */
    public static class ParentContextAvailableEvent extends ApplicationEvent {
        public ParentContextAvailableEvent(ConfigurableApplicationContext applicationContext,
                                           ApplicationContext parentContext);
        public ApplicationContext getParentContext();
    }
}

ParentContextCloserApplicationListener

Listener that automatically closes a child application context when its parent context is closed. This class ensures proper cleanup in context hierarchies by propagating close events down the hierarchy tree.

package org.springframework.boot.builder;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.core.Ordered;
import org.springframework.boot.builder.ParentContextApplicationContextInitializer.ParentContextAvailableEvent;

/**
 * Listener that closes the application context if its parent is closed.
 * It listens for refresh events and grabs the current context from there,
 * and then listens for closed events and propagates it down the hierarchy.
 *
 * @since 1.0.0
 */
public class ParentContextCloserApplicationListener
        implements ApplicationListener<ParentContextAvailableEvent>,
                   ApplicationContextAware,
                   Ordered {

    /**
     * Get the order value of this listener.
     * Returns Ordered.LOWEST_PRECEDENCE - 10
     *
     * @return the order value
     */
    @Override
    public int getOrder();

    /**
     * Set the ApplicationContext that this object runs in.
     *
     * @param context the ApplicationContext object to be used by this object
     * @throws org.springframework.beans.BeansException if thrown by application context methods
     */
    @Override
    public void setApplicationContext(ApplicationContext context)
            throws org.springframework.beans.BeansException;

    /**
     * Handle a ParentContextAvailableEvent.
     * Installs a listener in the parent context to close the child when parent closes.
     *
     * @param event the event to respond to
     */
    @Override
    public void onApplicationEvent(ParentContextAvailableEvent event);

    /**
     * Subclasses may override to create their own subclass of ContextCloserListener.
     * This still enforces the use of a weak reference.
     *
     * @param child the child context
     * @return the ContextCloserListener to use
     */
    protected ContextCloserListener createContextCloserListener(
            ConfigurableApplicationContext child);

    /**
     * ApplicationListener to close the context when parent closes.
     */
    protected static class ContextCloserListener
            implements ApplicationListener<ContextClosedEvent> {

        /**
         * Create a new ContextCloserListener.
         *
         * @param childContext the child context to close when parent closes
         */
        public ContextCloserListener(ConfigurableApplicationContext childContext);

        /**
         * Handle a ContextClosedEvent from the parent context.
         * Closes the child context if the parent is closing.
         *
         * @param event the event to respond to
         */
        @Override
        public void onApplicationEvent(ContextClosedEvent event);

        @Override
        public boolean equals(Object obj);

        @Override
        public int hashCode();
    }
}

Key Features

  • Automatic Cleanup: Automatically closes child contexts when parent closes
  • Weak References: Uses weak references to avoid preventing garbage collection
  • Hierarchy Propagation: Ensures close events cascade down context hierarchies
  • Order Control: Executes with order LOWEST_PRECEDENCE - 10 for proper sequencing
  • Extensibility: Supports subclassing to customize ContextCloserListener creation

How It Works

  1. Registration: The listener is registered as a bean in the child context
  2. Parent Detection: When a ParentContextAvailableEvent is fired, it detects the parent context
  3. Listener Installation: Installs a ContextCloserListener in the parent context
  4. Close Propagation: When parent closes, the listener closes the child context
  5. Weak References: Uses weak references to prevent memory leaks

Typical Usage Pattern

This listener is typically used automatically by SpringApplicationBuilder when building context hierarchies:

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class HierarchyApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext parentContext = new SpringApplicationBuilder()
            .sources(ParentConfig.class)
            .run(args);

        // Child context automatically gets ParentContextCloserApplicationListener
        ConfigurableApplicationContext childContext = new SpringApplicationBuilder()
            .sources(ChildConfig.class)
            .parent(parentContext)  // This triggers automatic listener registration
            .run(args);

        // When parent closes, child will automatically close too
        parentContext.close();
        // childContext is now also closed
    }
}

Manual Registration Example

You can also register the listener manually if needed:

import org.springframework.boot.builder.ParentContextCloserApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ManualRegistrationExample {

    public static void main(String[] args) {
        // Create parent context
        ConfigurableApplicationContext parent =
            new AnnotationConfigApplicationContext(ParentConfig.class);

        // Create child context
        ConfigurableApplicationContext child =
            new AnnotationConfigApplicationContext();
        child.setParent(parent);

        // Manually register the closer listener
        ParentContextCloserApplicationListener listener =
            new ParentContextCloserApplicationListener();
        child.addApplicationListener(listener);
        listener.setApplicationContext(child);

        // Register child context
        child.register(ChildConfig.class);
        child.refresh();

        // Trigger parent context available event
        child.publishEvent(new ParentContextApplicationContextInitializer
            .ParentContextAvailableEvent(child, parent));

        // Now when parent closes, child will automatically close
        parent.close();
    }
}

Advanced: Custom ContextCloserListener

You can extend the listener to customize close behavior:

import org.springframework.boot.builder.ParentContextCloserApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextClosedEvent;

public class CustomParentContextCloserListener
        extends ParentContextCloserApplicationListener {

    @Override
    protected ContextCloserListener createContextCloserListener(
            ConfigurableApplicationContext child) {
        return new CustomContextCloserListener(child);
    }

    protected static class CustomContextCloserListener extends ContextCloserListener {

        public CustomContextCloserListener(ConfigurableApplicationContext childContext) {
            super(childContext);
        }

        @Override
        public void onApplicationEvent(ContextClosedEvent event) {
            // Custom logic before closing
            System.out.println("Parent context closing, performing cleanup...");

            // Log context hierarchy information
            ConfigurableApplicationContext context = getChildContext();
            if (context != null) {
                System.out.println("Closing child context: " +
                    context.getDisplayName());
            }

            // Call parent implementation to actually close the context
            super.onApplicationEvent(event);

            // Custom logic after closing
            System.out.println("Child context closed successfully");
        }

        private ConfigurableApplicationContext getChildContext() {
            // Access to childContext field through reflection if needed
            // In practice, you might track this separately
            return null;
        }
    }
}

Memory Management

The ContextCloserListener uses WeakReference to hold the child context:

  • Prevents Memory Leaks: Parent holding strong reference to child could prevent garbage collection
  • Safe Cleanup: If child is garbage collected, the listener won't fail
  • Automatic Detachment: Garbage-collected children won't be closed (already gone)
// Internal implementation detail:
// WeakReference allows child to be garbage collected
// even if parent still exists

public class ContextCloserListener {
    private final WeakReference<ConfigurableApplicationContext> childContext;

    public ContextCloserListener(ConfigurableApplicationContext childContext) {
        // Stores weak reference, not strong reference
        this.childContext = new WeakReference<>(childContext);
    }

    public void onApplicationEvent(ContextClosedEvent event) {
        ConfigurableApplicationContext context = this.childContext.get();
        // If context was garbage collected, get() returns null
        if (context != null && context.isActive()) {
            context.close();
        }
    }
}

Integration with SpringApplicationBuilder

When using SpringApplicationBuilder.parent(), this listener is automatically registered:

import org.springframework.boot.builder.SpringApplicationBuilder;

// Parent context
SpringApplicationBuilder parentBuilder = new SpringApplicationBuilder()
    .sources(ParentConfig.class);

ConfigurableApplicationContext parent = parentBuilder.run(args);

// Child context - ParentContextCloserApplicationListener is automatically added
SpringApplicationBuilder childBuilder = new SpringApplicationBuilder()
    .sources(ChildConfig.class)
    .parent(parent);  // Triggers automatic listener registration

ConfigurableApplicationContext child = childBuilder.run(args);

// Automatic cleanup: closing parent will close child
parent.close();

Use Cases

  • Microservices Context Hierarchies: Parent context for shared services, child contexts for individual microservices
  • Multi-Tenant Applications: Parent context for infrastructure, child contexts for tenant-specific beans
  • Plugin Architectures: Parent context for core application, child contexts for plugins
  • Test Hierarchies: Parent context with common test infrastructure, child contexts for specific test scenarios
  • Modular Applications: Parent context for shared modules, child contexts for feature modules

Thread Safety

  • The listener itself is not thread-safe and should only be accessed by a single thread
  • However, the close propagation mechanism is thread-safe because:
    • Context close operations are synchronized
    • WeakReference operations are thread-safe
    • Event publishing is thread-safe in Spring

Import Statements

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.builder.ParentContextApplicationContextInitializer;