8
votes

I used AOP a lot with Java. It looks like that traditional java approaches can easily be reused with Kotlin. Given Kotlin emphasis on immutability JDK proxies seem to be the most feasible solution in Kotlin provided that you're following the same interface-first (better say trait-first in Kotlin) programming style, e.g.:

trait MyService {
 fun add(a: Int, b: Int): Int
}

class MyServiceImpl: MyService {...}

So now one can easily write an aspect in, say, Spring/AOP and apply it to the instances of MyServiceImpl. It should be mentioned that Java interface-based generated proxies may be more favored by Kotlin developers as cglib requires to get rid of final (i.e. resort to open classes in Kotlin) and use parameterless public constructors for every class which should be wrapped by AOP proxies.

At the same time Java interface-based generated proxies unfortunately impose significant performance penalty so I'm wondering if AOP programming can be done more intuitively or natively to Kotlin in some cases.

So, consider the following example, when I want to use AOP for:

  • Logging arguments and return result of each method exposed from a service.
  • Starting transaction before function call and closing it after function call completes (commit/rollback depending on whether or not an exception has been thrown while making a particular service call).

The most effective but unfortunately the most verbose brute-force solution in the aforementioned example with MyService/MyServiceImpl might look as follows:

class MyServiceLoggingWrapper(val delegate: MyService): MyService {
  val logger = LoggerFactory.getLogger(this.javaClass)

  override fun add(a: Int, b: Int): Int {
    val result = delegate.add(a, b);
    logger.info("MyService.add({}, {}) = {}", a, b, result);
    return result;
  }
} 

class MyServiceTxWrapper(val delegate: MyService, val txManager: TransactionManager): MyService {
 // similar lengthy implementation
}

The LoC complexity of that approach is O(N*K) where N is a number of method and K is a number of aspects that I want to apply.

So what I'm looking is a possible effective (both in terms of performance and LoC) solution for aspects in Kotlin preferrably without resorting to cglib-generated proxies as they impose too many limitations like saying goodbye to final classes and not having parameterless public constructors.

1
I'm guessing that any AOP tooling that works on Java binaries works on Kotlin binaries as well, but I never tried it. - Andrey Breslav
I tried it (they all work), but I found some of the limitations of existing libraries (cglib) to be frustrating for Kotlin developer who adopted Kotlin programming style. In order to test Kotlin I wrote a simple website (classic 3-tier architecture) with Spring MVC + Freemarker + JDBC + Spring TX (Transaction Management). It works as a charm. - Alex
Oh, and by the way - AOP libraries are extensively used by application developers. It usually includes but not limited to: establishing logging, profiling, exception translation, retries with backoff, transaction management etc. etc. So I feel that it'd be nice in future to have more intuitive style of establishing AOP proxies in Kotlin - it might be a super useful feature. Legacy ways of doing that imply serious trade offs - performance (java interface proxies) or expressiveness (cglib) - Alex
@AndreyBreslav I ran in to some issues with CGLib backed AOP, raised an issue: youtrack.jetbrains.com/issue/KT-10759 - Jasper Blues

1 Answers

1
votes

I am not a Kotlin user, but given the fact that it is targetting the JVM I would suggest trying full-blown AspectJ instead of a proxy-based "AOP lite" approach like Spring AOP. AspectJ does not need/use dynamic proxies, it generates byte code, either during compile time or after compilation (binary weaving) or even during class-loading (load-time weaving).