阅读(1161) (0)

Micronaut Scopes

2023-02-23 10:54:05 更新

Micronaut 具有基于 JSR-330 的可扩展 bean 作用域机制。支持以下默认范围:

内置范围

表 1. Micronaut 内置范围
类型 描述

@Singleton

Singleton 作用域表示将只存在一个 bean 实例

@Context

Context scope表示bean会和ApplicationContext同时创建(eager initialization)

@Prototype

Prototype作用域表示每次注入时都会创建一个新的bean实例

@Infrastructure

Infrastructure 作用域表示不能使用 @Replaces 覆盖或替换的 bean,因为它对系统的功能至关重要。

@ThreadLocal

@ThreadLocal 范围是一个自定义范围,它通过 ThreadLocal 为每个线程关联一个 bean

@Refreshable

@Refreshable 范围是一个自定义范围,它允许通过 /refresh 端点刷新 bean 的状态。

@RequestScope

@RequestScope 作用域是一个自定义作用域,表示创建了一个新的 bean 实例并与每个 HTTP 请求相关联

@Prototype 注解是@Bean 的同义词,因为默认作用域是原型。

可以通过定义实现 CustomScope 接口的 @Singleton bean 来添加其他范围。

请注意,当启动 ApplicationContext 时,默认情况下 @Singleton-scoped beans 是延迟和按需创建的。这是为了优化启动时间而设计的。

如果这对您的用例造成问题,您可以选择使用 @Context 注释,它将对象的生命周期绑定到 ApplicationContext 的生命周期。换句话说,当 ApplicationContext 启动时,您的 bean 将被创建。

或者,使用 @Parallel 注释任何 @Singleton-scoped bean,这允许在不影响整体启动时间的情况下并行初始化 bean。

如果您的 bean 未能并行初始化,应用程序将自动关闭。

单例的急切初始化

在某些情况下,@Singleton bean 的急切初始化可能是可取的,例如在 AWS Lambda 上,分配给 Lambda 构建的 CPU 资源多于执行。

您可以指定是否使用 ApplicationContextBuilder 接口预先初始化 @Singleton-scoped bean:

启用单例的急切初始化

public class Application {

    public static void main(String[] args) {
        Micronaut.build(args)
            .eagerInitSingletons(true) (1)
            .mainClass(Application.class)
            .start();
    }
}
  1. 将 eager init 设置为 true 会初始化所有单例

当您在无服务器函数等环境中使用 Micronaut 时,您将没有应用程序类,而是扩展了 Micronaut 提供的类。在这些情况下,Micronaut 提供了一些方法,您可以重写这些方法来增强 ApplicationContextBuilder

覆盖 newApplicationContextBuilder()

public class MyFunctionHandler extends MicronautRequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
...
    @Nonnull
    @Override
    protected ApplicationContextBuilder newApplicationContextBuilder() {
        ApplicationContextBuilder builder = super.newApplicationContextBuilder();
        builder.eagerInitSingletons(true);
        return builder;
    }
    ...
}

@ConfigurationReader beans,例如@EachProperty 或@ConfigurationProperties 是单例bean。要急切地初始化配置但保持其他 @Singleton-scoped bean 创建惰性,请使用 eagerInitConfiguration:

启用配置的急切初始化

public class Application {

    public static void main(String[] args) {
        Micronaut.build(args)
            .eagerInitConfiguration(true) (1)
            .mainClass(Application.class)
            .start();
    }
}
  1. 将 eager init 设置为 true 会初始化所有配置读取器 bean。

可刷新 Scope

Refreshable 范围是一个自定义范围,允许通过以下方式刷新 bean 的状态:

  • /refresh endpoint.
  • RefreshEvent 的发布。

以下示例说明了 @Refreshable 范围行为。

 Java Groovy  Kotlin 
@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
    open fun init() {
        forecast = "Scattered Clouds " + SimpleDateFormat("dd/MMM/yy HH:mm:ss.SSS").format(Date())// (2)
    }

    open fun latestForecast(): String? {
        return forecast
    }
}
  1. WeatherService 使用 @Refreshable 范围进行注释,该范围存储一个实例,直到触发刷新事件

  2. forecast 属性的值在创建 bean 时设置为固定值,并且在刷新 bean 之前不会更改

如果调用 latestForecast() 两次,您将看到相同的响应,例如“Scattered Clouds 01/Feb/18 10:29.199”。

当调用 /refresh 端点或发布 RefreshEvent 时,该实例将失效,并在下次请求该对象时创建一个新实例。例如:

 Java Groovy  Kotlin 
applicationContext.publishEvent(new RefreshEvent());
applicationContext.publishEvent(new RefreshEvent())
applicationContext.publishEvent(RefreshEvent())

元注释的范围

可以在元注释上定义范围,然后您可以将其应用于您的类。考虑以下示例元注释:

Driver.java Annotation

 Java Groovy  Kotlin 
import io.micronaut.context.annotation.Requires;

import jakarta.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)
public @interface Driver {
}
import io.micronaut.context.annotation.Requires

import jakarta.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 jakarta.inject.Singleton
import kotlin.annotation.AnnotationRetention.RUNTIME

@Requires(classes = [Car::class]) // (1)
@Singleton // (2)
@MustBeDocumented
@Retention(RUNTIME)
annotation class Driver
  1. 该范围使用 Requires 声明对 Car 类的要求

  2. 注解声明为@Singleton

在上面的示例中,@Singleton 注释应用于 @Driver 注释,这导致每个用 @Driver 注释的类都被视为单例。

请注意,在这种情况下,无法在应用注释时更改范围。例如,以下不会覆盖@Driver 声明的范围并且是无效的:

Declaring Another Scope

@Driver
@Prototype
class Foo {}

对于可覆盖的范围,请改为使用 @Driver 上的 DefaultScope 批注,它允许在不存在其他范围的情况下指定默认范围:

Using @DefaultScope

 Java Groovy  Kotlin 
@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 声明要使用的范围