3.6 Scopes

Micronaut features an extensible bean scoping mechanism based on JSR-330. The following default scopes are supported:

3.6.1 Built-In Scopes

Table 1. Micronaut Built-in Scopes
TypeDescription

@Singleton

Singleton scope indicates only one instance of the bean should exist

@Context

Context scope indicates that the bean should 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

The @Prototype annotation is a synonym for @Bean because the default scope is prototype.

Additional scopes can be added by defining a @Singleton bean that implements the CustomScope interface.

Note that with Micronaut when starting an ApplicationContext by default @Singleton scoped beans are created lazily and on demand. This is by design and to optimize startup time.

If this presents a problem for your use case you have the option of using the @Context annotation which binds the lifecycle of your object to the lifecycle of the ApplicationContext. In other words when the ApplicationContext is started your bean will be created.

Alternatively you can annotate any @Singleton scoped bean with @Parallel which allows parallel initialization of your bean without impacting overall startup time.

If your bean fails to initialize in parallel then the application will be automatically shutdown.

3.6.2 Refreshable Scope

The Refreshable scope is a custom scope that allows a bean’s state to be refreshed via:

The following example, illustrates the @Refreshable scope behavior.

  1. @Refreshable (1)
  2. public static class WeatherService {
  3. private String forecast;
  4. @PostConstruct
  5. public void init() {
  6. forecast = "Scattered Clouds " + new SimpleDateFormat("dd/MMM/yy HH:mm:ss.SSS").format(new Date());(2)
  7. }
  8. public String latestForecast() {
  9. return forecast;
  10. }
  11. }
  1. @Refreshable (1)
  2. static class WeatherService {
  3. String forecast
  4. @PostConstruct
  5. void init() {
  6. forecast = "Scattered Clouds ${new SimpleDateFormat("dd/MMM/yy HH:mm:ss.SSS").format(new Date())}" (2)
  7. }
  8. String latestForecast() {
  9. return forecast
  10. }
  11. }
  1. @Refreshable (1)
  2. open class WeatherService {
  3. private var forecast: String? = null
  4. @PostConstruct
  5. fun init() {
  6. forecast = "Scattered Clouds " + SimpleDateFormat("dd/MMM/yy HH:mm:ss.SSS").format(Date())(2)
  7. }
  8. open fun latestForecast(): String? {
  9. return forecast
  10. }
  11. }
1The WeatherService is annotated with @Refreshable scope which stores an instance until a refresh event is triggered
2The value of the forecast property is set to a fixed value when the bean is created and won’t change until the bean is refreshed

If you invoke the latestForecast() twice, you will see identical responses such as "Scattered Clouds 01/Feb/18 10:29.199".

When the /refresh endpoint is invoked or a RefreshEvent is published then the instance is invalidated and a new instance is created the next time the object is requested. For example:

  1. applicationContext.publishEvent(new RefreshEvent());
  1. applicationContext.publishEvent(new RefreshEvent())
  1. applicationContext.publishEvent(RefreshEvent())

3.6.3 Scopes on Meta Annotations

Scopes can be defined on Meta annotations that you can then apply to your classes. Consider the following example meta annotation:

  1. import static java.lang.annotation.RetentionPolicy.RUNTIME;
  2. import io.micronaut.context.annotation.Requires;
  3. import javax.inject.Singleton;
  4. import java.lang.annotation.Documented;
  5. import java.lang.annotation.Retention;
  6. @Requires(classes = Car.class ) (1)
  7. @Singleton (2)
  8. @Documented
  9. @Retention(RUNTIME)
  10. public @interface Driver {
  11. }
  1. import io.micronaut.context.annotation.Requires
  2. import javax.inject.Singleton
  3. import java.lang.annotation.Documented
  4. import java.lang.annotation.Retention
  5. import static java.lang.annotation.RetentionPolicy.RUNTIME
  6. @Requires(classes = Car.class ) (1)
  7. @Singleton (2)
  8. @Documented
  9. @Retention(RUNTIME)
  10. @interface Driver {
  11. }
  1. import io.micronaut.context.annotation.Requires
  2. import javax.inject.Singleton
  3. @Requires(classes = [Car::class]) (1)
  4. @Singleton (2)
  5. @MustBeDocumented
  6. @Retention(AnnotationRetention.RUNTIME)
  7. annotation class Driver
1The scope declares a requirement on a Car class using Requires
2The annotation is declared as @Singleton

In the example above the @Singleton annotation is applied to the @Driver annotation which results in every class that is annotated with @Driver being regarded as singleton.

Note that in this case it is not possible to alter the scope when the annotation is applied. For example, the following will not override the scope declared by @Driver and is invalid:

Declaring Another Scope

  1. @Driver
  2. @Prototype
  3. class Foo {}

If you wish for the scope to be overridable you should instead use the DefaultScope annotation on @Driver which allows a default scope to be specified if none other is present:

Using @DefaultScope

  1. @Requires(classes = Car.class )
  2. @DefaultScope(Singleton.class) (1)
  3. @Documented
  4. @Retention(RUNTIME)
  5. public @interface Driver {
  6. }
  1. @Requires(classes = Car.class )
  2. @DefaultScope(Singleton.class) (1)
  3. @Documented
  4. @Retention(RUNTIME)
  5. @interface Driver {
  6. }
  1. @Requires(classes = [Car::class])
  2. @DefaultScope(Singleton::class) (1)
  3. @Documented
  4. @Retention(RUNTIME)
  5. annotation class Driver
1DefaultScope is used to declare which scope to be used if none is present