Backend Development 10 min read

Spring Container Startup Listeners and Initialization Techniques

This article explains various ways to execute custom logic during Spring container startup, covering bean constructors, @PostConstruct, InitializingBean, ApplicationListener events, @EventListener, constructor injection, CommandLineRunner, and SmartLifecycle, with code examples and usage guidelines.

Architect
Architect
Architect
Spring Container Startup Listeners and Initialization Techniques

Spring is a widely used Java framework for dependency injection and inversion of control. When a Spring application starts, developers often need to perform initialization tasks such as creating scheduled jobs or connection pools.

The article introduces several mechanisms to run custom logic during Spring container startup:

Bean constructor initialization

Using @PostConstruct

Implementing InitializingBean

Listening to ApplicationListener events

Using @EventListener

Constructor injection

Implementing Spring Boot's CommandLineRunner

Implementing SmartLifecycle

Bean Constructor Initialization

Java class initialization order is static variables → static blocks → instance variables → instance blocks → constructors. Example of Log4j initialization in a static block is shown.

static {
    Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG);
    repositorySelector = new DefaultRepositorySelector(h);
    // ... additional Log4j configuration logic ...
}

Attempting to use @Autowired fields inside a constructor leads to NullPointerException because dependency injection occurs after the constructor.

@PostConstruct

Methods annotated with @PostConstruct run after bean initialization and dependency injection, allowing safe use of injected beans.

@Component
public class CustomBean {
    @Autowired
    private Environment env;

    @PostConstruct
    public void init() {
        env.getActiveProfiles();
    }
}

Correspondingly, @PreDestroy can be used for cleanup before bean destruction.

@Component
public class CustomBean {
    @Autowired
    private ExecutorService executor = Executors.newFixedThreadPool(1);

    @PreDestroy
    public void destroy() {
        // cleanup logic
    }
}

InitializingBean

Implementing InitializingBean and overriding afterPropertiesSet() provides another way to execute logic after properties are set.

@Component
public class CustomBean implements InitializingBean {
    private static final Logger LOG = Logger.getLogger(InitializingBeanExampleBean.class);
    @Autowired
    private Environment environment;

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info(environment.getDefaultProfiles());
    }
}

ApplicationListener

Spring publishes various lifecycle events (e.g., ApplicationStartingEvent , ContextRefreshedEvent , ApplicationReadyEvent , etc.). Implementing ApplicationListener allows handling these events.

@Component
@Slf4j
public class StartupApplicationListenerExample implements ApplicationListener
{
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        log.info("Subject ContextRefreshedEvent");
    }
}

Alternatively, the @EventListener annotation can be used on methods to listen to the same events.

@Component
@Slf4j
public class StartupApplicationListenerExample {
    @EventListener
    public void onApplicationEvent(ContextRefreshedEvent event) {
        log.info("Subject ContextRefreshedEvent");
    }
}

Constructor Injection

Constructor injection is the recommended way to inject dependencies, ensuring that all required beans are available when the object is created.

@Component
@Slf4j
public class ConstructorBean {
    private final Environment environment;

    @Autowired
    public ConstructorBean(Environment environment) {
        this.environment = environment;
        log.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

CommandLineRunner

In Spring Boot, implementing CommandLineRunner allows execution of code after the application context is fully started.

@Component
@Slf4j
public class CommandLineAppStartupRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        log.info("Increment counter");
    }
}

Multiple runners can be ordered using @Order .

SmartLifecycle

The SmartLifecycle interface provides advanced lifecycle management, allowing start and stop callbacks with phase ordering and auto-start control.

@Component
public class SmartLifecycleExample implements SmartLifecycle {
    private boolean isRunning = false;

    @Override
    public void start() {
        System.out.println("start");
        isRunning = true;
    }

    @Override
    public int getPhase() { return 0; }

    @Override
    public boolean isAutoStartup() { return true; }

    @Override
    public boolean isRunning() { return isRunning; }

    @Override
    public void stop(Runnable callback) {
        System.out.println("stop(Runnable)");
        callback.run();
        isRunning = false;
    }

    @Override
    public void stop() {
        System.out.println("stop");
        isRunning = false;
    }
}

These techniques together provide a comprehensive toolbox for executing custom initialization and cleanup logic within Spring applications.

JavaSpringDependency InjectionApplicationListenerBean InitializationCommandLineRunner
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.