运行AspectJ会导致NoSuchMethodError:Aspect.aspectOf

时间:2014-11-07 13:30:04

标签: java android gradle aspectj android-gradle

我有一个非常简单的AspectJ方面(使用@AspectJ),它只打印出一条日志消息。我的目标是在我的Android应用程序中建议代码。现在,只要我在应用程序源代码中拥有aspect类本身,这个方面就可以完美地运行。 一旦我将方面移动到另一个模块(java - > .jar或android lib - > .aar),在我的应用程序中运行建议代码时,我得到以下运行时异常

java.lang.NoSuchMethodError: com.xxx.xxx.TraceAspect.aspectOf

基本上我的结构是这样的:

Root
 + app (com.android.application)
   - MainActivity (with annotation to be adviced)
 + library (android-library)
   - TraceAspect (aspect definition)

从ajc编译器中,我可以看到ajc编译器选择了我的类并正确地建议它们,所以只要我的源代码中有@AspectJ类,我真的不知道它为什么会工作,但是我将它移动到jar存档后停止工作。

我正在使用gradle。我的应用程序的Buildscript非常简单。我按照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;
            }
        }
    }
}

不确定是否重要,但以防万一,我方面的代码:

@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();
  }
}

类路径

我还检查了javaCompile.classPath,它正确包含library-classes.jarapp-classes.jar。将-log file添加到ajc任务中也会显示文件已正确编织。

有什么想法吗?

重现此问题的最小示例

https://github.com/fschoellhammer/test-aspectj

3 个答案:

答案 0 :(得分:4)

该消息暗示方面文件尚未通过aspectj织布工。编织者将负责添加aspectOf()方法。虽然您的注释样式方面可以使用javac进行正常编译,但它们必须完成“#1;}由aspectj在某一时刻介绍支持编织的基础设施方法。如果你是加载时编织,这是在加载方面时完成的,但如果你是编译时或编译后编织时间,那么你需要以其他方式将它们转换为ajc。如果你有一个这样的库:

javac MyAspect.java
jar -cvMf code.jar MyAspect.class

然后你需要把那个罐子编织成“完成”。方面:

ajc -inpath code.jar -outjar myfinishedcode.jar

或者您可以使用ajc代替javac进行初始步骤

ajc MyAspect.java

或者您可以在方面应用于其他代码时执行此操作:

ajc <myAppSourceFiles> -inpath myaspects.jar 

通过在inpath上加入myaspects.jar,其中的任何方面类都将完成“#1”。作为此编译步骤的一部分,已完成的版本与已编译的应用程序源文件一起放置。请注意,这与使用宽高比路径不同:

ajc <myAppSourceFiles> -aspectpath myaspects.jar

此处方面路径的方面应用于您的代码,但它们仅从那里加载,它们已完成,因此您不会在编译后获得完成的版本应用程序源文件。

答案 1 :(得分:4)

我遇到了同样的问题,但我使用的是Maven而不是Gradle。

在将方面类应用于目标类之前,首先需要对其进行编织&#39;在一个方面。 一个weaved方面类将添加两个静态方法(aspectOf和hasAspect)。

在我的特殊情况下,我没有编织我的方面。

可以通过将aspectj-maven-plugin添加到构建部分来完成。

<build>
<plugins>
  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <executions>
      <execution>
        <goals>
          <goal>compile</goal> 
        </goals>
      </execution>
    </executions>
  </plugin>
</plugins>

希望它有所帮助!

答案 2 :(得分:3)

我使用了Gradle AspectJ plugin并将其应用于注释子项目,如下所示:

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

现在,应用程序在模拟器中工作,Android SDK中的DDMS显示建议输出在控制台上按预期方式进行。 :-)

Debug Monitor console log

请注意,我已将项目升级到AspectJ 1.8.4和Java 7.我还更改了这些设置:

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'
     }
 }