kotlinkotlin-coroutinesaspectjcompile-time-weaving

Suspend functions in Kotlin don't work with AspectJ


I have an annotation like:

@Retention(RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class MyAspect(val value: MyType)

I have an aspect for this annotation:

@Aspect
open class MyAspectInterceptor {

    @After("@annotation(com.example.MyAspect)")
    suspend fun wrapMethod(point: JoinPoint): Unit {
        // do some logic in coroutine
    }
}

I'm weaving in post compile using freefair plugin. In process of weaving errors appears:

[error] formal unbound in pointcut
[error] Found @AspectJ annotation on a non around advice not returning void 'wrapMethod(Lorg/aspectj/lang/JoinPoint;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;'
[error] the parameter $completion is not bound in [all branches of] pointcut

I then decompiled this class and found that the suspend modifier adds another parameter to this method and changes its signature:

@Aspect
@Metadata(mv = {1, 8, 0}, k = 1, xi = 48, d1 = {"\000 \n\002\030\002\n\002\020\000\n\002\b\002\n\002\030\002\n\000\n\002\020\002\n\000\n\002\030\002\n\002\b\002\b\027\030\0002\0020\001B\005\006\002\020\002J\031\020\005\032\0020\0062\006\020\007\032\0020\bH\001\000\006\002\020\tR\022\020\003\032\0020\0048\002@\002X\006\002\n\000\002\004\n\002\b\031\006\n"}, d2 = {"Lcom/example/MyAspect;", "", "()V", "auditService", "Lcom/example/MyAspectInterceptor;", "wrapMethod", "", "point", "Lorg/aspectj/lang/JoinPoint;", "(Lorg/aspectj/lang/JoinPoint;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", "service"})
public class MyAspectInterceptor {
  
  static {
    try {
      ajc$postClinit();
    } catch (Throwable throwable) {
      ajc$initFailureCause = throwable = null;
    } 
  }
  
  public static boolean hasAspect() {
    return (ajc$perSingletonInstance != null);
  }
  
  public static AuditAspect aspectOf() {
    if (ajc$perSingletonInstance == null)
      throw new NoAspectBoundException("com.example.MyAspect", ajc$initFailureCause); 
    return ajc$perSingletonInstance;
  }
  
  @After("@annotation(com.example.MyAspect)")
  @Nullable
  public final Object wrapMethod(@NotNull JoinPoint point, @NotNull Continuation $completion) {
    Intrinsics.checkNotNull(point.getSignature(), "null cannot be cast to non-null type org.aspectj.lang.reflect.MethodSignature");
    // business logic
    return Unit.INSTANCE;
  }
}

And here my question: Is it possible to make an aspect of AspectJ in Kotlin that will run in coroutine?


Solution

  • I've found a work around, we can use GlobalScope. Steps to make it work:

    1. Add Kotlin coroutine dependency – org.jetbrains.kotlinx:kotlinx-coroutines-core
    2. Mark method with @DelicateCoroutinesApi
    3. Call GlobalScope#launch inside an aspect method
    @DelicateCoroutinesApi
    @After("@annotation(...)")
    fun wrapMethod(point: JoinPoint): Unit {
        GlobalScope.launch {
            // our logic in coroutine
        }
    }