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.
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.
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.
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.