Working With Gradle, Spring Aspects and Compile-time Weaving
Join the DZone community and get the full member experience.
Join For FreeAs one of my previous posts already mentioned, I’m in the process of migrating most if not all of my projects to Gradle. I’m very pleased with the fact it’s going a lot smoother than I had anticipated.
However I came across an issue: how to get AspectJ compile-time weaving working with Gradle. My use case is quite simple: I’m using Spring’s @Configurable annotation, which either requires load-time or compile-time weaving to work correctly. Load-time weaving isn’t really an option, so compile time weaving it was. There is a plugin but it’s outdated: it doesn’t work with Gradle 1.6. If this was the case with Maven, I’d be screwed/ I would have to write my own plugin. Luckily with Gradle, it was not that difficult.
The old plugin relied on the old Gradle 0.9 DSL, so I could use that as a starting point. The instructions required me to define some new configurations and add some things to the javaCompile task. So I started out with this:
configurations { ajc aspects } compileJava { sourceCompatibility="1.6" targetCompatibility="1.6" doLast{ ant.taskdef( resource:"org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: configurations.ajc.asPath) ant.iajc(source:"1.6", target:"1.6", destDir:sourceSets.main.output.classesDir.absolutePath, maxmem:"512m", fork:"true", aspectPath:configurations.aspects.asPath, sourceRootCopyFilter:"**/.svn/*,**/*.java",classpath:configurations.compile.asPath){ sourceroots{ sourceSets.main.java.srcDirs.each{ pathelement(location:it.absolutePath) } } } } } dependencies { ajc "org.aspectj:aspectjtools:1.6.10" compile "org.aspectj:aspectjrt:1.6.10" aspects "org.springframework:spring-aspects:3.2.2.RELEASE" }
This, however, did not work. I was greeted by the following exception:
can't determine superclass of missing type org.springframework.transaction.interceptor.TransactionAspectSupport
It seems like spring-aspects contains multiple aspects, each requiring some dependencies which are required regardless whether you want to use those aspects. This, by the way, was also required when using the Maven AspectJ plugin. After some POM fishing, I found out I had to add a couple of libraries. So now we have the following dependencies:
dependencies { ajc "org.aspectj:aspectjtools:1.6.10" compile "org.aspectj:aspectjrt:1.6.10" aspects "org.springframework:spring-aspects:3.2.2.RELEASE" aspects "javax.persistence:persistence-api:1.0" aspects "org.springframework:spring-tx:3.2.2.RELEASE" aspects "org.springframework:spring-orm:3.2.2.RELEASE" }
My code compiled and my aspects were weaved in! Unfortunately, it wasn’t the end of the story. My tests failed. It was missing the aspect library at runtime, as the (now weaved) classes referenced some classes that were contained in spring-aspects and I was subclassing them in my testcode. The quick solution would be to duplicate the spring-aspects dependency in the compile configuration, but that was something I didn’t like. I did however came up with a cleaner solution: I had to make sure the aspect dependencies were added to the compile configuration. A small change in the configurations section was all I needed to do.
configurations { ajc aspects compile { extendsFrom aspects } }
Hurrah! I wished. Unfortunately now the dependencies I had to add to the build in order to make the weaving work were now also added to the compile configuration. I didn’t need them or used them, so I had to find a way to exclude them from the compile configuration, but still include them in the aspect weaving. The solution was simple and clean: add a new configuration and include those configuration’s dependencies in the weaving classpath. The new configiration is not added in the compile configuration, so your build is not dependent on those dependencies.
The final product looked like this:
configurations { ajc aspects aspectCompile compile { extendsFrom aspects } } compileJava { sourceCompatibility="1.6" targetCompatibility="1.6" doLast{ ant.taskdef( resource:"org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: configurations.ajc.asPath) ant.iajc(source:"1.6", target:"1.6", destDir:sourceSets.main.output.classesDir.absolutePath, maxmem:"512m", fork:"true", aspectPath:configurations.aspects.asPath}, sourceRootCopyFilter:"**/.svn/*,**/*.java",classpath:"${configurations.compile.asPath};${configurations.aspectCompile.asPath}"){ sourceroots{ sourceSets.main.java.srcDirs.each{ pathelement(location:it.absolutePath) } } } } } dependencies { ajc "org.aspectj:aspectjtools:1.6.10" compile "org.aspectj:aspectjrt:1.6.10" aspects "org.springframework:spring-aspects:3.2.2.RELEASE" aspectCompile "javax.persistence:persistence-api:1.0" aspectCompile "org.springframework:spring-tx:3.2.2.RELEASE" aspectCompile "org.springframework:spring-orm:3.2.2.RELEASE" }
The entire process took me about an hour, although most of it was looking up stuff in the Gradle documentation. I’m also really impressed with the error notification of Gradle. If something goes wrong, it’s quite accurate in pinpointing the problem.
This was one of the more complex things in the builds I have, so anything else should be a breeze. One thing I really, really like about Gradle is that you can merge all build configuration of a multi-module project into a single build.gradle
file. For a build with 10 modules this meant going from maintaining 11 Maven POM files to only one gradle build script. Even better: the single build.gradle
file is 3 times smaller than the projects root POM! If that’s not nice, I don’t know what is.
Published at DZone with permission of Lieven Doclo, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments