Sunday, 15 January 2023

Micronaut: Initialize singleton beans parallely

@Singleton-scoped beans are created lazily and on-demand by default. If you want to initialize these beans eagerly, you can go with @Parallel annotation, which allows parallel initialization of your bean without impacting overall startup time.

@Singleton
@Named("mySingletonBean1")
@Parallel
public DemoBean singletonBean1() {
	DemoBean bean = new DemoBean("singletonBean1");
	return bean;
}

 

@Parallel annotation indicate that the beans can be initialized in parallel with the application context. This allows optimized startup time by allowing beans that have slow initialization paths to startup and without impacting the overall startup time of the application.

 

If a parallel bean fails to startup, it will by default stop the ApplicationContext with an error unless shutdownOnError attribute set to false.

@Singleton
@Named("mySingletonBean2")
@Parallel(shutdownOnError = false)
public DemoBean singletonBean2() {
	DemoBean bean = new DemoBean("singletonBean2");
	return bean;
}

Find the below working application.

 

Step 1: Create new maven project ‘micronaut-parallel-init-of-beans’.

 

Step 2: Update pom.xml with maven dependencies.

 

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.sample.app</groupId>
	<artifactId>micronaut-parallel-init-of-beans</artifactId>
	<version>1</version>

	<parent>
		<groupId>io.micronaut</groupId>
		<artifactId>micronaut-parent</artifactId>
		<version>3.7.0</version>
	</parent>

	<properties>
		<packaging>jar</packaging>
		<jdk.version>11</jdk.version>
		<release.version>11</release.version>
		<micronaut.version>3.7.0</micronaut.version>
		<micronaut.runtime>netty</micronaut.runtime>
		<exec.mainClass>com.sample.app.App</exec.mainClass>
	</properties>

	<repositories>
		<repository>
			<id>central</id>
			<url>https://repo.maven.apache.org/maven2</url>
		</repository>
	</repositories>

	<dependencies>
		<dependency>
			<groupId>io.micronaut</groupId>
			<artifactId>micronaut-inject</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>io.micronaut</groupId>
			<artifactId>micronaut-validation</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>io.micronaut</groupId>
			<artifactId>micronaut-http-client</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>io.micronaut</groupId>
			<artifactId>micronaut-http-server-netty</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>io.micronaut</groupId>
			<artifactId>micronaut-jackson-databind</artifactId>
			<scope>compile</scope>
		</dependency>

		<dependency>
			<groupId>jakarta.annotation</groupId>
			<artifactId>jakarta.annotation-api</artifactId>
			<scope>compile</scope>
		</dependency>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>io.micronaut.test</groupId>
			<artifactId>micronaut-test-junit5</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-api</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-engine</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>io.micronaut.build</groupId>
				<artifactId>micronaut-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Step 3: Define model class.

 

DemoBean.java

package com.sample.app.model;

import java.util.concurrent.atomic.AtomicInteger;

public class DemoBean {
	private static AtomicInteger counter = new AtomicInteger(1);

	private int id;

	private String description;

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public DemoBean() {
		System.out.println("DemoBean default constructor called for ");
	}

	public DemoBean(String description) {
		this.description = description;
		this.id = counter.getAndIncrement();
		System.out.println("DemoBean constructor called for " + description);
	}

	@Override
	public String toString() {
		int identityHashCode = System.identityHashCode(this);

		return "DemoBean [id=" + id + ", description=" + description + ", identityHashCode= " + identityHashCode + "]";
	}

}

 

Step 4: Define BeansFactory.

 

BeansFactory.java

package com.sample.app.factory;

import com.sample.app.model.DemoBean;

import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Parallel;
import jakarta.inject.Named;
import jakarta.inject.Singleton;

@Factory
public class BeansFactory {

	@Singleton
	@Named("mySingletonBean1")
	@Parallel
	public DemoBean singletonBean1() {
		DemoBean bean = new DemoBean("singletonBean1");
		return bean;
	}

	@Singleton
	@Named("mySingletonBean2")
	@Parallel(shutdownOnError = false)
	public DemoBean singletonBean2() {
		DemoBean bean = new DemoBean("singletonBean2");
		return bean;
	}

	@Singleton
	@Named("mySingletonBean3")
	public DemoBean singletonBean3() {
		DemoBean bean = new DemoBean("singletonBean3");
		return bean;
	}

}

 

Step 5: Define HelloWorldController class.

 

HelloWorldController.java

package com.sample.app.controller;

import com.sample.app.model.DemoBean;

import io.micronaut.context.ApplicationContext;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.inject.qualifiers.Qualifiers;
import jakarta.inject.Inject;

@Controller
public class HelloWorldController {

	@Inject
	private ApplicationContext applicationContext;

	private String output() {
		DemoBean mySingletonBean1 = applicationContext.getBean(DemoBean.class, Qualifiers.byName("mySingletonBean1"));
		DemoBean mySingletonBean2 = applicationContext.getBean(DemoBean.class, Qualifiers.byName("mySingletonBean2"));
		DemoBean mySingletonBean3 = applicationContext.getBean(DemoBean.class, Qualifiers.byName("mySingletonBean3"));

		StringBuilder builder = new StringBuilder();
		builder.append(mySingletonBean1).append("\n");
		builder.append(mySingletonBean2).append("\n");
		builder.append(mySingletonBean3).append("\n");

		return builder.toString();
	}

	@Get(value = "/hello", produces = MediaType.TEXT_PLAIN)
	public String index() {

		return output();
	}

}

 

Step 6: Define main application class.

 

App.java

 

package com.sample.app;

import io.micronaut.runtime.Micronaut;

public class App {
	public static void main(String[] args) {
		Micronaut.run(App.class, args);
	}
}

 

Total project structure looks like below.


Build the project using mvn package command.

Navigate to the folder where pom.xml is located and execute the command ‘mvn package’.

 

Upon command successful execution, you can see the jar file ‘micronaut-factory-annotation-demo-1-jar-with-dependencies.jar’ in project target folder.

$ls ./target/
classes						maven-status
generated-sources				micronaut-parallel-init-of-beans-1.jar
generated-test-sources				original-micronaut-parallel-init-of-beans-1.jar
maven-archiver					test-classes

Execute below command to start the application.

java -jar ./target/micronaut-parallel-init-of-beans-1.jar

You will see below messages in the console.

DemoBean constructor called for singletonBean2
DemoBean constructor called for singletonBean1

As we do not annotate ‘singletonBean3’ with @Parallel annotation, it will be initialized on demand. To confirm the same, open the url ‘http://localhost:8080/hello’ in browser, you will see below message in the console.

DemoBean constructor called for singletonBean3

You can download this application from this link.



Previous                                                    Next                                                    Home

No comments:

Post a Comment