Examples of introduction advice include things like or Spring Data that will both automatically implement persistence logic for you.

    Micronaut’s annotation is another example of introduction advice where Micronaut will, at compile time, implement HTTP client interfaces for you.

    The way you implement Introduction advice is very similar to how you implement Around advice.

    You start off by defining an annotation that will power the introduction advice. As an example, say you want to implement advice that will return a stubbed value for every method in an interface (a common requirement in testing frameworks). Consider the following annotation:

    Introduction Advice Annotation Example

    Introduction Advice Annotation Example

    1. import io.micronaut.aop.Introduction
    2. import io.micronaut.context.annotation.Bean
    3. import io.micronaut.context.annotation.Type
    4. import java.lang.annotation.Documented
    5. import java.lang.annotation.ElementType
    6. import java.lang.annotation.Retention
    7. import java.lang.annotation.Target
    8. import static java.lang.annotation.RetentionPolicy.RUNTIME
    9. @Introduction (1)
    10. @Type(StubIntroduction.class) (2)
    11. @Bean (3)
    12. @Documented
    13. @Retention(RUNTIME)
    14. @Target([ElementType.TYPE, ElementType.ANNOTATION_TYPE, ElementType.METHOD])
    15. @interface Stub {
    16. String value() default ""
    17. }

    Introduction Advice Annotation Example

    1. import io.micronaut.aop.Introduction
    2. import io.micronaut.context.annotation.Bean
    3. import io.micronaut.context.annotation.Type
    4. import java.lang.annotation.Documented
    5. import java.lang.annotation.Retention
    6. import java.lang.annotation.RetentionPolicy.RUNTIME
    7. @Introduction (1)
    8. @Bean (3)
    9. @Documented
    10. @Retention(RUNTIME)
    11. annotation class Stub(val value: String = "")

    The following is an example implementation:

    StubIntroduction

    1. import io.micronaut.aop.*;
    2. import javax.inject.Singleton;
    3. @Singleton
    4. public class StubIntroduction implements MethodInterceptor<Object,Object> { (1)
    5. @Override
    6. public Object intercept(MethodInvocationContext<Object, Object> context) {
    7. return context.getValue( (2)
    8. Stub.class,
    9. context.getReturnType().getType()
    10. ).orElse(null); (3)
    11. }
    12. }

    StubIntroduction

    StubIntroduction

    1. import io.micronaut.aop.MethodInterceptor
    2. import io.micronaut.aop.MethodInvocationContext
    3. import javax.inject.Singleton
    4. @Singleton
    5. class StubIntroduction : MethodInterceptor<Any, Any> { (1)
    6. override fun intercept(context: MethodInvocationContext<Any, Any>): Any? {
    7. return context.getValue<Any>( (2)
    8. Stub::class.java,
    9. ).orElse(null) (3)
    10. }
    11. }
    1The class is annotated with @Singleton and implements the MethodInterceptor interface
    2The value of the annotation is read from the context and an attempt made to convert the value to the return type
    3Otherwise null is returned

    To now use this introduction advice in an application you simply annotate your abstract classes or interfaces with @Stub:

    StubExample

    1. @Stub
    2. public interface StubExample {
    3. @Stub("10")
    4. int getNumber();
    5. LocalDateTime getDate();
    6. }

    StubExample

    1. @Stub
    2. interface StubExample {
    3. @Stub("10")
    4. int getNumber()
    5. LocalDateTime getDate()
    6. }

    All abstract methods will delegate to the StubIntroduction class to be implemented.

    The following test demonstrates the behaviour or StubIntroduction:

    Testing Introduction Advice

    1. StubExample stubExample = applicationContext.getBean(StubExample.class);
    2. assertEquals(10, stubExample.getNumber());
    3. assertNull(stubExample.getDate());

    Testing Introduction Advice

    1. when:
    2. StubExample stubExample = applicationContext.getBean(StubExample.class)
    3. then:
    4. stubExample.getNumber() == 10
    5. stubExample.getDate() == null

    Testing Introduction Advice

    1. val stubExample = applicationContext.getBean(StubExample::class.java)
    2. stubExample.date.shouldBe(null)

    Note that if the introduction advice cannot implement the method the proceed method of the should be called. This gives the opportunity for other introduction advice interceptors to implement the method, otherwise a will be thrown if no advice can implement the method.

    In addition, if multiple introduction advice are present you may wish to override the getOrder() method of MethodInterceptor to control the priority of advise.