阅读(298) (0)

Micronaut Bean 生命周期 Advice

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

有时您可能需要将建议应用于 bean 的生命周期。有 3 种类型的建议适用于这种情况:

  • 拦截 bean 的构造

  • 拦截 bean 的 @PostConstruct 调用

  • 拦截 bean 的 @PreDestroy 调用

Micronaut 通过允许定义额外的 @InterceptorBinding 元注解来支持这 3 个用例。

考虑以下注解定义:

AroundConstruct 示例

 Java Groovy  Kotlin 
import io.micronaut.aop.*;
import io.micronaut.context.annotation.Prototype;
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@AroundConstruct // (1)
@InterceptorBinding(kind = InterceptorKind.POST_CONSTRUCT) // (2)
@InterceptorBinding(kind = InterceptorKind.PRE_DESTROY) // (3)
@Prototype // (4)
public @interface ProductBean {
}
import io.micronaut.aop.*
import io.micronaut.context.annotation.Prototype
import java.lang.annotation.*

@Retention(RetentionPolicy.RUNTIME)
@AroundConstruct // (1)
@InterceptorBinding(kind = InterceptorKind.POST_CONSTRUCT) // (2)
@InterceptorBinding(kind = InterceptorKind.PRE_DESTROY) // (3)
@Prototype // (4)
@interface ProductBean {
}
import io.micronaut.aop.AroundConstruct
import io.micronaut.aop.InterceptorBinding
import io.micronaut.aop.InterceptorBindingDefinitions
import io.micronaut.aop.InterceptorKind
import io.micronaut.context.annotation.Prototype

@Retention(AnnotationRetention.RUNTIME)
@AroundConstruct // (1)
@InterceptorBindingDefinitions(
    InterceptorBinding(kind = InterceptorKind.POST_CONSTRUCT), // (2)
    InterceptorBinding(kind = InterceptorKind.PRE_DESTROY) // (3)
)
@Prototype // (4)
annotation class ProductBean
  1. 添加@AroundConstruct 注释以指示应该拦截构造函数

  2. @InterceptorBinding 定义用于指示应该发生@PostConstruct 拦截

  3. @InterceptorBinding 定义用于指示应该发生@PreDestroy 拦截

  4. 该 bean 被定义为 @Prototype,因此每个注入点都需要一个新实例

请注意,如果您不需要 @PostConstruct 和 @PreDestroy 拦截,您可以简单地删除这些绑定。

然后可以在目标类上使用 @ProductBean 注释:

使用 AroundConstruct 元注释

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

import jakarta.annotation.PreDestroy;

@ProductBean // (1)
public class Product {
    private final String productName;
    private boolean active = false;

    public Product(@Parameter String productName) { // (2)
        this.productName = productName;
    }

    public String getProductName() {
        return productName;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    @PreDestroy // (3)
    void disable() {
        active = false;
    }
}
import io.micronaut.context.annotation.Parameter
import jakarta.annotation.PreDestroy

@ProductBean // (1)
class Product {
    final String productName
    boolean active = false

    Product(@Parameter String productName) { // (2)
        this.productName = productName
    }

    @PreDestroy // (3)
    void disable() {
        active = false
    }
}
import io.micronaut.context.annotation.Parameter
import jakarta.annotation.PreDestroy

@ProductBean // (1)
class Product(@param:Parameter val productName: String ) { // (2)

    var active: Boolean = false
    @PreDestroy
    fun disable() { // (3)
        active = false
    }
}
  1. @ProductBean 注释定义在 Product 类型的类上

  2. @Parameter 注解表明这个 bean 需要一个参数来完成构造

  3. 任何@PreDestroy 或@PostConstruct 方法在拦截器链中最后执行

现在您可以为构造函数拦截定义 ConstructorInterceptor bean,为 @PostConstruct 或 @PreDestroy 拦截定义 MethodInterceptor bean。

以下工厂定义了一个 ConstructorInterceptor,它拦截 Product 实例的构造并将它们注册到一个假设的 ProductService 中,首先验证产品名称:

定义构造函数拦截器

 Java Groovy  Kotlin 
import io.micronaut.aop.*;
import io.micronaut.context.annotation.Factory;

@Factory
public class ProductInterceptors {
    private final ProductService productService;

    public ProductInterceptors(ProductService productService) {
        this.productService = productService;
    }
}

@InterceptorBean(ProductBean.class)
ConstructorInterceptor<Product> aroundConstruct() { // (1)
    return context -> {
        final Object[] parameterValues = context.getParameterValues(); // (2)
        final Object parameterValue = parameterValues[0];
        if (parameterValue == null || parameterValues[0].toString().isEmpty()) {
            throw new IllegalArgumentException("Invalid product name");
        }
        String productName = parameterValues[0].toString().toUpperCase();
        parameterValues[0] = productName;
        final Product product = context.proceed(); // (3)
        productService.addProduct(product);
        return product;
    };
}
import io.micronaut.aop.*
import io.micronaut.context.annotation.Factory


@Factory
class ProductInterceptors {
    private final ProductService productService

    ProductInterceptors(ProductService productService) {
        this.productService = productService
    }
}

@InterceptorBean(ProductBean.class)
ConstructorInterceptor<Product> aroundConstruct() { // (1)
    return  { context ->
        final Object[] parameterValues = context.parameterValues // (2)
        final Object parameterValue = parameterValues[0]
        if (parameterValue == null || parameterValues[0].toString().isEmpty()) {
            throw new IllegalArgumentException("Invalid product name")
        }
        String productName = parameterValues[0].toString().toUpperCase()
        parameterValues[0] = productName
        final Product product = context.proceed() // (3)
        productService.addProduct(product)
        return product
    }
}
import io.micronaut.aop.*
import io.micronaut.context.annotation.Factory

@Factory
class ProductInterceptors(private val productService: ProductService) {
}

@InterceptorBean(ProductBean::class)
fun aroundConstruct(): ConstructorInterceptor<Product> { // (1)
    return ConstructorInterceptor { context: ConstructorInvocationContext<Product> ->
        val parameterValues = context.parameterValues // (2)
        val parameterValue = parameterValues[0]
        require(!(parameterValue == null || parameterValues[0].toString().isEmpty())) { "Invalid product name" }
        val productName = parameterValues[0].toString().uppercase()
        parameterValues[0] = productName
        val product = context.proceed() // (3)
        productService.addProduct(product)
        product
    }
}
  1. 定义了一个新的 @InterceptorBean,它是一个 ConstructorInterceptor

  2. 可以根据需要检索和修改构造函数参数值

  3. 可以使用 proceed() 方法调用构造函数

定义拦截 @PostConstruct 和 @PreDestroy 方法的 MethodInterceptor 实例与为常规方法定义拦截器没有什么不同。但是请注意,您可以使用传递的 MethodInvocationContext 来识别正在发生的拦截类型,并相应地调整代码,如以下示例所示:

定义构造函数拦截器

 Java Groovy  Kotlin 
@InterceptorBean(ProductBean.class) // (1)
MethodInterceptor<Product, Object> aroundInvoke() {
    return context -> {
        final Product product = context.getTarget();
        switch (context.getKind()) {
            case POST_CONSTRUCT: // (2)
                product.setActive(true);
                return context.proceed();
            case PRE_DESTROY: // (3)
                productService.removeProduct(product);
                return context.proceed();
            default:
                return context.proceed();
        }
    };
}
@InterceptorBean(ProductBean.class) // (1)
MethodInterceptor<Product, Object> aroundInvoke() {
    return { context ->
        final Product product = context.getTarget()
        switch (context.kind) {
            case InterceptorKind.POST_CONSTRUCT: // (2)
                product.setActive(true)
                return context.proceed()
            case InterceptorKind.PRE_DESTROY: // (3)
                productService.removeProduct(product)
                return context.proceed()
            default:
                return context.proceed()
        }
    }
}
@InterceptorBean(ProductBean::class)
fun  aroundInvoke(): MethodInterceptor<Product, Any> { // (1)
    return MethodInterceptor { context: MethodInvocationContext<Product, Any> ->
        val product = context.target
        return@MethodInterceptor when (context.kind) {
            InterceptorKind.POST_CONSTRUCT -> { // (2)
                product.active = true
                context.proceed()
            }
            InterceptorKind.PRE_DESTROY -> { // (3)
                productService.removeProduct(product)
                context.proceed()
            }
            else -> context.proceed()
        }
    }
}
  1. 定义了一个新的 @InterceptorBean,它是一个 MethodInterceptor

  2. @PostConstruct拦截处理

  3. @PreDestroy拦截处理