Sunday, 15 January 2023

Micronaut : quick guide to built-in scopes

At the time of writing this post, Micronaut support following built-in scopes.

 

Scope

Description

@Singleton

Singleton scope indicates only one instance of the bean will exist.

@Context

Context scope indicates that the bean will be created at the same time as the ApplicationContext (eager initialization)

@Prototype

Prototype scope indicates that a new instance of the bean is created each time it is injected

@Infrastructure

Infrastructure scope represents a bean that cannot be overridden or replaced using @Replaces because it is critical to the functioning of the system.

@ThreadLocal

@ThreadLocal scope is a custom scope that associates a bean per thread via a ThreadLocal.

@Refreshable

@Refreshable scope is a custom scope that allows a bean’s state to be refreshed via the /refresh endpoint.

@RequestScope

@RequestScope scope is a custom scope that indicates a new instance of the bean is created and associated with each HTTP request

 

Let’s see an example to understand how these scopes work.

 

Step 1: Create new maven project ‘micronaut-scopes-demo’.

 

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-scopes-demo</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 a 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 BeanFactory class.

 

BeanFactory.java

package com.sample.app.factory;

import com.sample.app.model.DemoBean;

import io.micronaut.context.annotation.Context;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Infrastructure;
import io.micronaut.context.annotation.Prototype;
import io.micronaut.runtime.context.scope.Refreshable;
import io.micronaut.runtime.http.scope.RequestScope;
import jakarta.inject.Named;
import jakarta.inject.Singleton;

@Factory
public class BeansFactory {

	@Singleton
	@Named("mySingletonBean")
	public DemoBean singletonBean() {
		DemoBean bean = new DemoBean("singletonBean");
		return bean;
	}

	@Context
	@Named("myContextBean")
	public DemoBean contextBean() {
		DemoBean bean = new DemoBean("contextBean");
		return bean;
	}

	@Prototype
	@Named("myPrototypeBean")
	public DemoBean prototypeBean() {
		DemoBean bean = new DemoBean("prototypeBean");
		return bean;
	}

	@Infrastructure
	@Named("myInfrastructureBean")
	public DemoBean infrastructureBean() {
		DemoBean bean = new DemoBean("infrastructureBean");
		return bean;
	}

	@RequestScope
	@Named("myRequestScopeBean")
	public DemoBean requestScopeBean() {
		DemoBean bean = new DemoBean("requestScopeBean");
		return bean;
	}
	
	@Refreshable
	@Named("myRefreshableScopeBean")
	public DemoBean refreshableScopeBean() {
		DemoBean bean = new DemoBean("refreshableScopeBean");
		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 io.micronaut.runtime.context.scope.refresh.RefreshEvent;
import jakarta.inject.Inject;

@Controller
public class HelloWorldController {

	@Inject
	private ApplicationContext applicationContext;

	private String output() {
		DemoBean mySingletonBean = applicationContext.getBean(DemoBean.class, Qualifiers.byName("mySingletonBean"));
		DemoBean myContextBean = applicationContext.getBean(DemoBean.class, Qualifiers.byName("myContextBean"));
		DemoBean myPrototypeBean = applicationContext.getBean(DemoBean.class, Qualifiers.byName("myPrototypeBean"));
		DemoBean myInfrastructureBean = applicationContext.getBean(DemoBean.class,
				Qualifiers.byName("myInfrastructureBean"));
		DemoBean myRequestScopeBean = applicationContext.getBean(DemoBean.class,
				Qualifiers.byName("myRequestScopeBean"));
		DemoBean myRefreshableScopeBean = applicationContext.getBean(DemoBean.class,
				Qualifiers.byName("myRefreshableScopeBean"));

		StringBuilder builder = new StringBuilder();
		builder.append(mySingletonBean).append("\n");
		builder.append(myContextBean).append("\n");
		builder.append(myPrototypeBean).append("\n");
		builder.append(myInfrastructureBean).append("\n");
		builder.append(myRequestScopeBean).append("\n");
		builder.append(myRefreshableScopeBean).append("\n");

		return builder.toString();
	}

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

		return output();
	}

	@Get(value = "/publish-refresh-event", produces = MediaType.TEXT_PLAIN)
	public String refresh() {

		applicationContext.publishEvent(new RefreshEvent());

		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-scopes-demo-1.jar’ in project target folder.

 

Run below command to start the Micronaut application.

java -jar ./target/micronaut-scopes-demo-1.jar

Open the url ‘http://localhost:8080/hello’ in browser, you will see below messages in the browser window.

DemoBean [id=2, description=singletonBean, identityHashCode= 1551171790]
DemoBean [id=1, description=contextBean, identityHashCode= 1132967838]
DemoBean [id=3, description=prototypeBean, identityHashCode= 1564098532]
DemoBean [id=4, description=infrastructureBean, identityHashCode= 691873789]
DemoBean [id=5, description=requestScopeBean, identityHashCode= 1650588664]
DemoBean [id=6, description=refreshableScopeBean, identityHashCode= 711943942]

Reload the url ‘http://localhost:8080/hello’ in browser, you will see below messages in the browser window.

DemoBean [id=2, description=singletonBean, identityHashCode= 1551171790]
DemoBean [id=1, description=contextBean, identityHashCode= 1132967838]
DemoBean [id=7, description=prototypeBean, identityHashCode= 706956753]
DemoBean [id=8, description=infrastructureBean, identityHashCode= 1864488689]
DemoBean [id=9, description=requestScopeBean, identityHashCode= 372640666]
DemoBean [id=6, description=refreshableScopeBean, identityHashCode= 711943942]

From the above output, you can confirm prototypBean, infrastructureBean and requestScopeBean are assigned with new identifiers.

 

Let’s publish the refresh event by hitting the api ‘http://localhost:8080/publish-refresh-event’. You will see below messages.

DemoBean [id=2, description=singletonBean, identityHashCode= 1551171790]
DemoBean [id=1, description=contextBean, identityHashCode= 1132967838]
DemoBean [id=10, description=prototypeBean, identityHashCode= 188764689]
DemoBean [id=11, description=infrastructureBean, identityHashCode= 922165262]
DemoBean [id=12, description=requestScopeBean, identityHashCode= 2013716100]
DemoBean [id=13, description=refreshableScopeBean, identityHashCode= 1674974275]

As you see refreshableScopeBean is reinitialized upon publishing the refresh event.

 

You can download this application from below link.

https://github.com/harikrishna553/java-libs/tree/master/micronaut/ioc/micronaut-scopes-demo

 

Points to note

a.   @Singleton-scoped beans are created lazily and on-demand by default.

b.   bean’s state to be refreshed via:

1.   /refresh endpoint

2.   Publication of a RefreshEvent.

 

  

Previous                                                    Next                                                    Home

No comments:

Post a Comment