Monday, March 7, 2016

Gradle and AspectJ

Unfortunately, there's no built-in support for AspectJ in Gradle. But there are a lot of resources, even plugins (e.g., Gradle AspectJ plugin), how to get them working together. The only complaint I have about them is that they substitute the AspectJ compiler and bytecode weaver for the native Java compiler. It doesn't work sometimes. For example, I like Lombok but it and the Aspectj compile-and-weaving process are at odds. So I had to change the solution described in Working With Gradle, Spring Aspects and Compile-time Weaving a bit.
The main idea is the same. We introduce new configurations:
configurations {
    ajc
    aspects
    compile {
        extendsFrom aspects
    }
}
and add required dependencies:
compile "org.aspectj:aspectjrt:$aspectjVersion"
compile "org.aspectj:aspectjweaver:$aspectjVersion"

ajc "org.aspectj:aspectjtools:$aspectjVersion"
aspects "org.springframework:spring-aspects:$springVersion"
Then we define a closure:
def aspectj = { destDir, aspectPath, inpath, classpath ->
    ant.taskdef(resource: "org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties",
            classpath: configurations.ajc.asPath)

    ant.iajc(
            maxmem: "1024m", fork: "true", Xlint: "ignore",
            destDir: destDir,
            aspectPath: aspectPath,
            inpath: inpath,
            classpath: classpath,
            source: project.sourceCompatibility,
            target: project.targetCompatibility
    )
}
and change the standard Compile tasks:
compileJava {
    doLast {
        aspectj project.sourceSets.main.output.classesDir.absolutePath,
                configurations.aspects.asPath,
                project.sourceSets.main.output.classesDir.absolutePath,
                project.sourceSets.main.runtimeClasspath.asPath
    }
}

compileTestJava {
    dependsOn jar

    doLast {
        aspectj project.sourceSets.test.output.classesDir.absolutePath,
                configurations.aspects.asPath + jar.archivePath,
                project.sourceSets.test.output.classesDir.absolutePath,
                project.sourceSets.test.runtimeClasspath.asPath
    }
}
That's all. Lombok and AspectJ have been reconciled.

You can find a working example here.

6 comments:

  1. java.lang.IllegalStateException: Method [aspectOf] was discovered in the .class file but cannot be resolved in the class object

    Caused By: java.lang.NoSuchMethodException: myclass.aspectOf()

    ReplyDelete
    Replies
    1. I faced this exception also, because my aspect had a 1 parameter constructor but no default constructor.
      Once I added a default constructor, the error went away.

      Delete
  2. Hi, how can I exclude project's Aspects. I want use only library Aspects.

    ReplyDelete
    Replies
    1. aspectj closure allows you to specify paths that should be included in weaving like ajc ant task it uses. The aspectPath parameter specifies what aspects will be applied.

      Delete
  3. I do wonder why some people would rather use Ant from Gradle than AspectJ Maven plugin which is a real plugin and does not force your to script your build. Of course I understand that Gradle users want to be able to build their AspectJ projects with it, but I do think someone should write a better, well maintained and configurable plugin for it. If Gradle is so modern and developers like it so much, I wonder why this has not happened yet.

    ReplyDelete
    Replies
    1. this topic is about solve the conflict of Aspectj & lombok . AspectJ Maven plugin won't solve the problem .

      Delete