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
Type | Description |
---|---|
Singleton scope indicates only one instance of the bean should exist | |
Context scope indicates that the bean should be created at the same time as the | |
Prototype scope indicates that a new instance of the bean is created each time it is injected | |
Infrastructure scope represents a bean that cannot be overridden or replaced using @Replaces because it is critical to the functioning of the system. | |
| |
| |
|
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:
/refresh
endpoint.Publication of a RefreshEvent.
The following example, illustrates the @Refreshable
scope behavior.
@Refreshable (1)
public static class WeatherService {
private String forecast;
@PostConstruct
public void init() {
forecast = "Scattered Clouds " + new SimpleDateFormat("dd/MMM/yy HH:mm:ss.SSS").format(new Date());(2)
}
public String latestForecast() {
return forecast;
}
}
@Refreshable (1)
static class WeatherService {
String forecast
@PostConstruct
void init() {
forecast = "Scattered Clouds ${new SimpleDateFormat("dd/MMM/yy HH:mm:ss.SSS").format(new Date())}" (2)
}
String latestForecast() {
return forecast
}
}
@Refreshable (1)
open class WeatherService {
private var forecast: String? = null
@PostConstruct
fun init() {
forecast = "Scattered Clouds " + SimpleDateFormat("dd/MMM/yy HH:mm:ss.SSS").format(Date())(2)
}
open fun latestForecast(): String? {
return forecast
}
}
1 | The WeatherService is annotated with @Refreshable scope which stores an instance until a refresh event is triggered |
2 | The 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:
applicationContext.publishEvent(new RefreshEvent());
applicationContext.publishEvent(new RefreshEvent())
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:
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import io.micronaut.context.annotation.Requires;
import javax.inject.Singleton;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@Requires(classes = Car.class ) (1)
@Singleton (2)
@Documented
@Retention(RUNTIME)
public @interface Driver {
}
import io.micronaut.context.annotation.Requires
import javax.inject.Singleton
import java.lang.annotation.Documented
import java.lang.annotation.Retention
import static java.lang.annotation.RetentionPolicy.RUNTIME
@Requires(classes = Car.class ) (1)
@Singleton (2)
@Documented
@Retention(RUNTIME)
@interface Driver {
}
import io.micronaut.context.annotation.Requires
import javax.inject.Singleton
@Requires(classes = [Car::class]) (1)
@Singleton (2)
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class Driver
1 | The scope declares a requirement on a Car class using Requires |
2 | The 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
@Driver
@Prototype
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
@Requires(classes = Car.class )
@DefaultScope(Singleton.class) (1)
@Documented
@Retention(RUNTIME)
public @interface Driver {
}
@Requires(classes = Car.class )
@DefaultScope(Singleton.class) (1)
@Documented
@Retention(RUNTIME)
@interface Driver {
}
@Requires(classes = [Car::class])
@DefaultScope(Singleton::class) (1)
@Documented
@Retention(RUNTIME)
annotation class Driver
1 | DefaultScope is used to declare which scope to be used if none is present |