I used AOP a lot with Java. It seems that traditional Java approaches can be easily used with Kotlin. Given that Kotlin pays particular attention to immutability, the JDK proxies seem to be the most acceptable solution in Kotlin, provided that you follow the same interface - first (better to say, the first in Kotlin) programming style, for example:
trait MyService { fun add(a: Int, b: Int): Int } class MyServiceImpl: MyService {...}
So, now you can easily write an aspect, such as Spring / AOP, and apply it to instances of MyServiceImpl. It should be noted that generated proxies based on the Java interface may be preferable by Kotlin developers, since cglib requires getting rid of the final one (i.e., resorting to open classes in Kotlin) and using parameterless public constructors for each class that should be wrapped by proxies AOP servers.
At the same time, the generated proxies based on the Java interface, unfortunately, impose a significant decrease in performance, so I wonder if it is possible in some cases to program AOP more intuitively or initially for Kotlin.
So, consider the following example when I want to use AOP for:
- Writing arguments and returning the result of each method exposed using the service.
- Starting a transaction before calling the function and closing it after the call to the function ends (commit / roll back depending on whether an exception was selected when making a specific service call).
The most effective, but unfortunately the most accurate brute force solution in the above example with MyService / MyServiceImpl might look like this:
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 this approach is O (N * K), where N is the number of methods and K is the number of aspects that I want to apply.
So, what I'm looking for is a possible effective (both in terms of performance and LoC) solution for aspects in Kotlin, preferably without resorting to proxies created by cglib, because they impose too many restrictions, such as parting with final classes and absence without parameters.