Search code examples
springkotlingradlespring-aop

Spring AOP (AspectJ) with Kotlin and Gradle - I can't get it to work


I have a Spring Boot + Kotlin + Gradle project. I'd like to create a small library for my use-cases. This library should use AOP to remove some cross cutting concerns I observed.

Therefore I started adding these two dependencies to my gradle build file.

build.gradle.kts

implementation("org.springframework:spring-aop:5.2.9.RELEASE")
implementation("org.springframework:spring-aspects:5.2.9.RELEASE")

I also added the freefair aspectj plugin due some suggestions from the interwebs. The following aspect I created should be placed in src/main/aspectj according to this documentation: https://docs.freefair.io/gradle-plugins/5.2.1/reference/#_io_freefair_aspectj

This plugin adds AspectJ support to the project, by adding a aspectj directory to every source set. Source contained in the src/main/aspectj directory will be compiled with ajc by the compileAspectj task.

plugins {
    // ...
    id("io.freefair.aspectj") version "5.2.1"
    // ...
}

I then started to create my first aspect that matches on every method which is annotated with @Foozy

src/main/aspectj/FoozyAspect.kt < the 'special' source path

@Component
@Aspect
class FoozyAspect {

    @Before("@annotation(com.client.annotation.Foozy)")
    fun doStuff() {
        LOG.info("Do Stuff")
    }

    companion object {
        private val LOG = LoggerFactory.getLogger(FoozyAspect::class.java)
    }
}

Then I created this annotation

src/main/kotlin/com.client.annotation/Foozy.kt

@Target(AnnotationTarget.FUNCTION)
annotation class Foozy

Now to test if everything works as expected I created a unit test

src/test/kotlin/FoozyAspectTest.kt

@SpringBootTest
@EnableAspectJAutoProxy
internal class FoozyAspectTest {
    private val testCandidate: TestCandidate = TestCandidate()

    @Test
    fun `should work with aspect`() {
        testCandidate.doStuff()
    }
}

src/test/TestCandidate.kt

class TestCandidate {
    @Foozy
    fun doStuff(): String {
        return "stuff"
    }
}

Result

Executing the text in debug mode does not yield the awaited info log Do Stuff and also does not cease the thread at the breakpoint in the FoozyAspect.kt doStuff() method. I have no idea what to configure here. For good reason I kinda have the suspicion that I am mixing up different "ways" to get this to work or am just missing some final steps in preconfiguration/prerequisites.


Solution

  • The AspectJ compiler can't compile Kotlin source code. Your .kt file in src/main/aspectj will be completely ignored.

    You have different options depending on what you really want to do:

    Do you want your Aspect to be woven by ajc at compile-time, or do you just want to use "plain" Spring AOP?

    The differences are explained here: https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-choosing

    If you just want to use Spring AOP, you dont need a special gradle plugin. Just put your .kt file in src/main/kotlin and follow the Spring AOP docs.

    If you want to weave your aspect at compile-time with ajc, you have two options:

    1. Keep the io.freefair.aspectj plugin to compile an weave the aspect in one step: Implement your aspect as .java or .aj so it can be compiled by ajc
    2. Switch to the io.freefair.aspectj.post-compile-weaving plugin in order to separate the compilation an weaving steps: In this case you can keep your Aspect implementation as Kotlin, but you have to put it in src/main/kotlin. Your Aspect will then be compiled by kotlinc and then woven by ajc.