I have a very simple AspectJ aspect (using @AspectJ) which just prints out a log message. My goal is to advice code in my android application. Now this aspects works perfectly fine as long as I have the aspect class itself in my applications source-code. Once I move the aspect into a different module (either java -> .jar or android lib -> .aar) I get the following runtime exception when running the adviced code in my application:
java.lang.NoSuchMethodError: com.xxx.xxx.TraceAspect.aspectOf
Basically my structure is like this:
Root
+ app (com.android.application)
- MainActivity (with annotation to be adviced)
+ library (android-library)
- TraceAspect (aspect definition)
From the ajc compiler, I can see that the ajc compiler picks up my classes and advices them correctly, so I really don't know why it works as long as I have the @AspectJ class in my sourcecode, but stops working once I move it to a jar archive.
I am using gradle. Buildscript for my app is super simple. I followed the instructions in http://fernandocejas.com/2014/08/03/aspect-oriented-programming-in-android/
import com.android.build.gradle.LibraryPlugin
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.12.+'
classpath 'org.aspectj:aspectjtools:1.8.1'
}
}
apply plugin: 'com.android.application'
repositories {
mavenCentral()
}
dependencies {
compile 'org.aspectj:aspectjrt:1.8.1'
compile project (':library')
}
android.applicationVariants.all { variant ->
AppPlugin plugin = project.plugins.getPlugin(AppPlugin)
JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
"-1.5",
"-XnoInline",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", plugin.project.android.bootClasspath.join(File.pathSeparator)]
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler)
def log = project.logger
for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}
Not sure if important, but just in case, the code of my aspect:
@Aspect
public class TraceAspect {
private static final String POINTCUT_METHOD = "execution(@com.xxx.TraceAspect * *(..))";
@Pointcut(POINTCUT_METHOD)
public void annotatedMethod() {}
@Around("annotatedMethod()")
public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Aspect works...");
return joinPoint.proceed();
}
}
Classpath
I also checked the javaCompile.classPath
and it correctly contains both the library-classes.jar
and my app-classes.jar
. Adding -log file
to the ajc
tasks also shows that files are correctly weaved.
Any ideas?
Minimal example to reproduce this problem
https://github.com/fschoellhammer/test-aspectj
The message implies that the aspect file has not gone through the aspectj
weaver. The weaver would be responsible for adding the aspectOf()
method. Although your annotation style aspects will compile fine with javac
, they must be 'finished off' by aspectj
at some point to introduce the infrastructure methods that support weaving. If you were load-time weaving this is done as the aspects are loaded but if you are compile time or post-compile time weaving then you need to get them to ajc
some other way. If you have a library built like this:
javac MyAspect.java
jar -cvMf code.jar MyAspect.class
then you'd need to get that jar woven to 'complete' the aspects:
ajc -inpath code.jar -outjar myfinishedcode.jar
Or you could just use ajc
instead of javac
for the initial step
ajc MyAspect.java
Or you could do it at the point the aspects are being applied to your other code:
ajc <myAppSourceFiles> -inpath myaspects.jar
By including myaspects.jar on the inpath
, any aspect classes in there will be 'finished off' as part of this compile step and the finished versions put alongside your compiled application source files. Note this is different to if you used the aspect path:
ajc <myAppSourceFiles> -aspectpath myaspects.jar
Here the aspects on the aspect path are applied to your code but they are only loaded from there, they are not finished off and so you wouldn't get the finished versions alongside your compiled application source files.
I run into the same problem but I used Maven instead of Gradle.
Before an aspect class can be applied to a target class it first needs to be 'weaved' into an aspect. A weaved aspect class will have two static methods added (aspectOf and hasAspect).
In my particular case I didn't weave my aspects.
It can be done by adding aspectj-maven-plugin to the build section.
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
Hope it helps!
I played around with a Gradle AspectJ plugin and applied it to the annotation subproject like this:
buildscript {
repositories {
maven {
url "https://maven.eveoh.nl/content/repositories/releases"
}
}
dependencies {
classpath "nl.eveoh:gradle-aspectj:1.4"
}
}
project.ext {
aspectjVersion = '1.8.4'
}
apply plugin: 'aspectj'
project.convention.plugins.java.sourceCompatibility = org.gradle.api.JavaVersion.VERSION_1_7
project.convention.plugins.java.targetCompatibility = org.gradle.api.JavaVersion.VERSION_1_7
Now the app works in the emulator and DDMS from the Android SDK shows that the advice output is on the console as expected. :-)
Please note that I have upgraded the project to AspectJ 1.8.4 and Java 7. I have also changed these settings:
Index: app/build.gradle
===================================================================
--- app/build.gradle (revision 9d9c3ce4e0f903b5e7c650f231577c20585e6923)
+++ app/build.gradle (revision )
@@ -2,8 +2,7 @@
dependencies {
// aspectJ compiler
- compile 'org.aspectj:aspectjrt:1.8.1'
-
+ compile 'org.aspectj:aspectjrt:1.8.4'
compile (project (':annotation'))
}
@@ -50,13 +49,13 @@
JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
- "-1.5",
+ "-1.7",
"-XnoInline",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
- //"-log", "/home/flo/workspace/test-aspectj/weave.log",
+ "-log", "weave.log",
"-bootclasspath", plugin.project.android.bootClasspath.join(File.pathSeparator)]
MessageHandler handler = new MessageHandler(true);
Index: build.gradle
===================================================================
--- build.gradle (revision 9d9c3ce4e0f903b5e7c650f231577c20585e6923)
+++ build.gradle (revision )
@@ -5,7 +5,7 @@
dependencies {
classpath 'com.android.tools.build:gradle:0.12.+'
// aspectj - http://fernandocejas.com/2014/08/03/aspect-oriented-programming-in-android/
- classpath 'org.aspectj:aspectjtools:1.8.1'
+ classpath 'org.aspectj:aspectjtools:1.8.4'
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With