How To Run Custom Code on Micronaut Start
When building applications with Micronaut, there are times when you need to run specific logic as soon as the application starts — for example, initializing resources, preloading caches, validating configurations, or logging startup metrics. Let us delve into understanding how to run startup code when micronaut starts.
1. Understanding Micronaut and Flyway
Micronaut provides a flexible way to hook into the application lifecycle using various events. These events can be used to trigger custom logic at specific stages of your application’s startup or shutdown. The two most common approaches to execute code during the startup phase are:
- Implementing ApplicationEventListener: You can create a bean that listens to specific lifecycle events like
StartupEvent
by implementingApplicationEventListener<StartupEvent>
. This gives you full control and allows for logic execution at precise moments. - Using @EventListener: A more concise, annotation-based approach to listen for events. Simply annotate a method in a bean with
@EventListener
and define the event type as a method parameter (e.g.,StartupEvent
).
These methods are ideal for initializing resources, pre-loading configuration, registering services, or performing health checks—tasks that must run before your application starts serving incoming requests. For more on Micronaut’s lifecycle and events, refer to the official Micronaut Event Listeners Guide.
2. Code Example
Here’s how you can create a complete Micronaut project that includes the necessary dependencies, configuration files, and structure to run startup code.
2.1 Add dependencies (build.gradle)
Generate a Micronaut project using either the Micronaut CLI or the Micronaut Launch website. By default, Micronaut includes the essential dependencies. However, we’ll verify that all required dependencies are present in the build.gradle
file and make any necessary edits to ensure the project is properly configured.
dependencies { implementation 'io.micronaut:micronaut-runtime' implementation 'io.micronaut:micronaut-inject' implementation 'jakarta.inject:jakarta.inject-api:compatible__jar__version' runtimeOnly 'ch.qos.logback:logback-classic:compatible__jar__version'' testImplementation 'io.micronaut:micronaut-test' testImplementation 'org.junit.jupiter:junit-jupiter-api:compatible__jar__version'' testImplementation 'org.junit.jupiter:junit-jupiter-engine:compatible__jar__version'' testImplementation 'org.mockito:mockito-core:compatible__jar__version'' }
2.2 Write Startup Listener Code
Now, let’s create the Java classes for the startup event listener.
2.2.1 Using ApplicationEventListener Interface
Create a class called StartupListener.java
inside the src/main/java/com/example/startup
directory.
package com.example.startup; import io.micronaut.context.event.ApplicationEventListener; import io.micronaut.runtime.event.ApplicationStartupEvent; import jakarta.inject.Singleton; @Singleton public class StartupListener implements ApplicationEventListener { @Override public void onApplicationEvent(ApplicationStartupEvent event) { System.out.println("Application has started. Running startup code..."); initializeResources(); } private void initializeResources() { System.out.println("Initializing resources..."); // Simulate some startup logic } }
The above code defines a Micronaut singleton class StartupListener
that implements ApplicationEventListener
with a generic type of ApplicationStartupEvent
. This allows the class to listen for the application’s startup event. When the Micronaut application starts, the onApplicationEvent
method is automatically triggered, printing a message to the console and calling the initializeResources()
method. This method simulates the initialization of resources required at startup, making it a good place to execute custom logic such as loading configuration, connecting to external systems, or pre-warming caches.
2.2.2 Using EventListener Annotation
Create a class called AnnotatedStartupListener.java
inside the src/main/java/com/example/startup
directory.
package com.example.startup; import io.micronaut.runtime.event.annotation.EventListener; import io.micronaut.runtime.event.ApplicationStartupEvent; import jakarta.inject.Singleton; @Singleton public class AnnotatedStartupListener { @EventListener public void onStartup(ApplicationStartupEvent event) { System.out.println("App started - @EventListener triggered!"); performStartupTasks(); } private void performStartupTasks() { System.out.println("Executing annotated startup tasks..."); // Custom logic here } }
The above code defines a Micronaut bean AnnotatedStartupListener
annotated with @Singleton
, which uses the @EventListener
annotation to listen for the ApplicationStartupEvent
. When the application starts, the onStartup
method is automatically invoked, printing a message and calling the performStartupTasks()
method. This method is intended to hold any custom startup logic such as initializing services, loading data, or setting up connections. The use of @EventListener
provides a declarative and cleaner way to handle lifecycle events compared to implementing interfaces.
2.3 Create Main Application Class
The Application.java
class will serve as the entry point for the application.
package com.example.startup; import io.micronaut.runtime.Micronaut; public class Application { public static void main(String[] args) { Micronaut.run(Application.class, args); } }
2.4 Configuration File
If you want to configure the application as per your requirements, you can modify the src/main/resources/application.yml
file for Micronaut settings.
micronaut: application: name: startup server: port: 8080
2.5 Running the Application
Once the code is set up, you can run your Micronaut application using the following Gradle command:
./gradlew run
You should see the startup messages printed in the terminal, such as:
-- ApplicationEventListener interface -- Application has started. Running startup code... Initializing resources... -- @EventListener annotation – App started - @EventListener triggered! Executing annotated startup tasks...
3. Conclusion
Micronaut provides powerful and flexible mechanisms to hook into the application lifecycle. Whether you prefer the explicit interface-based approach using ApplicationEventListener
or the more concise @EventListener
annotation, you can easily insert custom logic during startup. These mechanisms help keep your application clean, modular, and reactive to its own lifecycle. By leveraging these features, you can efficiently initialize necessary resources, preload data, or configure services that your application will need throughout its runtime. Choose the approach that best suits your application’s needs and scale your startup logic accordingly.