阅读(3376) (0)

Micronaut 介绍 Advice

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

Introduction advice 与 Around advice 不同,因为它涉及提供实现而不是装饰。

介绍建议的示例包括为您实现持久性逻辑的 GORM 和 Spring Data。

Micronaut 的客户端注解是另一个引入建议的例子,其中 Micronaut 在编译时为你实现了 HTTP 客户端接口。

实现 Introduction 建议的方式与实现 Around 建议的方式非常相似。

您首先定义一个支持介绍建议的注释。举个例子,假设你想实现通知,为接口中的每个方法返回一个存根值(测试框架中的常见要求)。考虑以下 @Stub 注释:

Introduction Advice Annotation Example

 Java Groovy  Kotlin 
import io.micronaut.aop.Introduction;
import io.micronaut.context.annotation.Bean;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Introduction // (1)
@Bean // (2)
@Documented
@Retention(RUNTIME)
@Target({TYPE, ANNOTATION_TYPE, METHOD})
public @interface Stub {
    String value() default "";
}
import io.micronaut.aop.Introduction
import io.micronaut.context.annotation.Bean

import java.lang.annotation.Documented
import java.lang.annotation.Retention
import java.lang.annotation.Target

import static java.lang.annotation.ElementType.ANNOTATION_TYPE
import static java.lang.annotation.ElementType.METHOD
import static java.lang.annotation.ElementType.TYPE
import static java.lang.annotation.RetentionPolicy.RUNTIME

@Introduction // (1)
@Bean // (2)
@Documented
@Retention(RUNTIME)
@Target([TYPE, ANNOTATION_TYPE, METHOD])
@interface Stub {
    String value() default ""
}
import io.micronaut.aop.Introduction
import io.micronaut.context.annotation.Bean
import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS
import kotlin.annotation.AnnotationTarget.CLASS
import kotlin.annotation.AnnotationTarget.FILE
import kotlin.annotation.AnnotationTarget.FUNCTION
import kotlin.annotation.AnnotationTarget.PROPERTY_GETTER
import kotlin.annotation.AnnotationTarget.PROPERTY_SETTER

@Introduction // (1)
@Bean // (2)
@MustBeDocumented
@Retention(RUNTIME)
@Target(CLASS, FILE, ANNOTATION_CLASS, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
annotation class Stub(val value: String = "")
  1. 介绍建议用Introduction注释

  2. 添加Bean注解,使得所有用@Stub注解的类型都成为bean

前面示例中引用的 StubIntroduction 类必须实现 MethodInterceptor 接口,就像环绕通知一样。

以下是一个示例实现:

StubIntroduction

 Java Groovy  Kotlin 
import io.micronaut.aop.*;
import io.micronaut.core.annotation.Nullable;
import jakarta.inject.Singleton;

@Singleton
@InterceptorBean(Stub.class) // (1)
public class StubIntroduction implements MethodInterceptor<Object, Object> { // (2)

    @Nullable
    @Override
    public Object intercept(MethodInvocationContext<Object, Object> context) {
        return context.getValue( // (3)
                Stub.class,
                context.getReturnType().getType()
        ).orElse(null); // (4)
    }
}
import io.micronaut.aop.MethodInterceptor
import io.micronaut.aop.MethodInvocationContext
import io.micronaut.aop.InterceptorBean
import io.micronaut.core.annotation.Nullable
import jakarta.inject.Singleton

@Singleton
@InterceptorBean(Stub) // (1)
class StubIntroduction implements MethodInterceptor<Object,Object> { // (2)

    @Nullable
    @Override
    Object intercept(MethodInvocationContext<Object, Object> context) {
        context.getValue( // (3)
                Stub,
                context.returnType.type
        ).orElse(null) // (4)
    }
}
import io.micronaut.aop.*
import jakarta.inject.Singleton

@Singleton
@InterceptorBean(Stub::class) // (1)
class StubIntroduction : MethodInterceptor<Any, Any> { // (2)

    override fun intercept(context: MethodInvocationContext<Any, Any>): Any? {
        return context.getValue<Any>( // (3)
                Stub::class.java,
                context.returnType.type
        ).orElse(null) // (4)
    }
}
  1. InterceptorBean 注释用于将拦截器与 @Stub 注释相关联

  2. 该类使用 @Singleton 注释并实现 MethodInterceptor 接口

  3. @Stub 注释的值是从上下文中读取的,并尝试将值转换为返回类型

  4. 否则返回 null

现在要在应用程序中使用此介绍建议,请使用 @Stub 注释您的抽象类或接口:

StubExample

 Java Groovy  Kotlin 
@Stub
public interface StubExample {

    @Stub("10")
    int getNumber();

    LocalDateTime getDate();
}
@Stub
interface StubExample {

    @Stub("10")
    int getNumber()

    LocalDateTime getDate()
}
@Stub
interface StubExample {

    @get:Stub("10")
    val number: Int

    val date: LocalDateTime?
}

所有抽象方法都委托给要实现的 StubIntroduction 类。

以下测试演示了 StubIntroduction 的行为:

Testing Introduction Advice

 Java Groovy  Kotlin 
StubExample stubExample = applicationContext.getBean(StubExample.class);

assertEquals(10, stubExample.getNumber());
assertNull(stubExample.getDate());
when:
def stubExample = applicationContext.getBean(StubExample)

then:
stubExample.number == 10
stubExample.date == null
val stubExample = applicationContext.getBean(StubExample::class.java)

stubExample.number.shouldBe(10)
stubExample.date.shouldBe(null)

注意如果introduction advice不能实现方法,调用MethodInvocationContext的proceed方法。这让其他引入通知拦截器实现该方法,如果没有通知可以实现该方法,将抛出 UnsupportedOperationException。

此外,如果存在多个引入通知,您可能希望覆盖 MethodInterceptor 的 getOrder() 方法以控制通知的优先级。

以下部分介绍了 Micronaut 提供的核心建议类型。