0

I'm developing some aspects which are expected to load-time weave classes with @AnAnnotation. I created aop.xml as follows

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>

    <weaver options="-verbose -showWeaveInfo">
        <include within="@com.example.MyAnnotation *" />
    </weaver>

    <aspects>
        <aspect name="com.example.MyAspect" />
    </aspects>

</aspectj>

and according to log, my aspect seems to try advise appropriate classes but right after that the following exception occurred.

java.lang.NoSuchMethodError: com.example.MyAspect.aspectOf

Any ideas?

:log

[ERROR] [AppClassLoader@2b193f2d] info AspectJ Weaver Version 1.9.7 built on Thursday Jun 24, 2021 at 16:14:45 PDT
[ERROR] [AppClassLoader@2b193f2d] info register classloader jdk.internal.loader.ClassLoaders$AppClassLoader@2b193f2d
[ERROR] [AppClassLoader@2b193f2d] info using configuration project/target/classes/META-INF/aop.xml
[ERROR] [AppClassLoader@2b193f2d] info register aspect com.example.MyAspect
[ERROR] [AppClassLoader@2b193f2d] weaveinfo Join point 'constructor-execution(void com.example.MyAspectTest$MyTargetClass.<init>(com.example.MyAspectTest, java.lang.String))' in Type 'com.example.MyAspectTest$MyTargetClass' (MyAspectTest.java:165) advised by afterReturning advice from 'com.example.MyAspect' (MyAspect.java) [with runtime test]
[ERROR] [AppClassLoader@2b193f2d] weaveinfo Join point 'method-execution(void com.example.MyAspectTest$MyTargetClass.myMethod(int))' in Type 'com.example.MyAspectTest$MyTargetClass.' (MyAspectTest.java:182) advised by afterReturning advice from 'com.example.MyAspect' (MyAspect.java) [with runtime test]
[INFO] Running com.example.MyAspectTest
[ERROR] Tests run: 5, Failures: 0, Errors: 5, Skipped: 0, Time elapsed: 0.055 s <<< FAILURE! - in com.example.MyAspectTest
[ERROR] com.example.MyAspectTest.testMyMethod  Time elapsed: 0.011 s  <<< ERROR!
java.lang.NoSuchMethodError: 'com.example.MyAspect com.example.MyAspect.aspectOf()'
    at com.example.MyAspectTest.<init>(MyAspectTest.java:57)

:MyAspect.java

package com.example;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class MyAspect {

    @AfterReturning("""
            @target(com.example.MyAnnotation)
            && (execution(* *.*(..)) || execution(* .new(..)))
            """)
    public void intercept(JoinPoint JoinPoint) throws Throwable {
        System.out.println("********** MyAspect intercepted");
    }
}
kriegaex
  • 63,017
  • 15
  • 111
  • 202
avenue68
  • 43
  • 7
  • It seems that you have a bootstrapping problem there, like the aspect not being instantiated yet when it is already referenced from your woven `com.example.MyAspectTest` constructor. I would like to inspect the situation in more detail, so please provide an [MCVE](https://stackoverflow.com/help/mcve) (ideally on GitHub), so I can reproduce it. BTW, why did you tag the question with _aspectj-maven-plugin_? For LTW, you do not need that plugin, only for CTW. Maybe you mixed up two weaving methods there somehow. Are you using Spring (Boot)? – kriegaex Dec 17 '21 at 05:45
  • Thanks for your comments. I removed *aspectj-maven-plugin* tag, sorry I didn't understand about it enough. And I uploaded the MVCE on [GitHub](https://github.com/avenue68/repository-for-sharing/tree/master/demo). Please check it! – avenue68 Dec 17 '21 at 08:04
  • The MCVE made it easy to find the problem's root cause, which otherwise I might have overlooked. So you did yourself a favour by providing it. – kriegaex Dec 20 '21 at 01:55
  • Ok I will arrange MVCE in advance when I post questions! – avenue68 Dec 20 '21 at 07:45

1 Answers1

1

There is a subtle error in your aop.xml:

<include within="@com.example.MyAnnotation *" />

The include within rule is too narrow. Aspect weaving is falsely limited to only target classes, the aspect class itself is excluded, because it does not carry the target annotation itself. The effect is that the weaver never finishes the aspect itself - remember, it was not compiled by AJC before. This caused NoSuchMethodError: ...MyAspect.aspectOf(). So widening the weaving scope to the whole base package fixes your problem:

<include within="com.example..*" />

Two more things you should improve:

  • Use @within rather than @target, because @target is evaluated dynamically during runtime. Only then we know for certain which exact type a target instance has. @within however can be evaluated statically during weaving.
  • Use AspectJ 1.9.8.RC3 due to use of Java 17 (you seem to like the new text blocks). For LTW, this might not be strictly necessary as long as the byte code is compatible, but latest when trying to compile Java 17 code, AspectJ 1.9.7 would not suffice, because it only supports up to Java 15.

Here is my pull request with the three corresponding commits.


Update: If for whatever reason you want to avoid mentioning the target package in aop.xml, simply just mention the annotation and the aspect like this:

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
  <weaver options="-verbose -showWeaveInfo">
    <include within="com.example.MyAspect"/>
    <include within="@com.example.MyAnnotation *"/>
  </weaver>
  <aspects>
    <aspect name="com.example.MyAspect"/>
  </aspects>
</aspectj>

A way to avoid explicitly including the aspect is to simply compile the aspect library with the AspectJ Compiler AJC (e.g. via AspectJ Maven Plugin).


Update 2, regarding your follow-up question: As described in the AspectJ 1.9.7 release notes, due to JEP 396 you need to add --add-opens java.base/java.lang=ALL-UNNAMED to your Java command line.

I fixed your problem in pull request #2.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • The weaver include specifies all classes which are to be passed through the weaver. The aspect itself, if not compiled by the AspectJ compiler but just by Javac, also needs to be handled by the weaver, because it needs to be "finished", i.e. turned from a simple Java class into a full-fledged AspectJ aspect. Your include clause did not encompass the aspect class itself, though. – kriegaex Dec 20 '21 at 07:51
  • Thanks for your answer and lecture! Let me question a little more. I thought the `weaver include within` rule just defines which classes should be woven. Does it have other means? It would be nice if you could explain about it or propose any related references. And actually I want to make `MyAspect` available as a jar library. So I want `aop.xml` to depend not any packages but only `@MyTarget` annotation. Could Adding `MyAspect` into `weaver include` to include the aspect class itself be a solution in theory? I tried it but resulted in catching `NullPointerException` in my original project. – avenue68 Dec 20 '21 at 07:53
  • Your `aop.xml` already mentions the package name as part of the fully qualified annotation name, so you were using it before already. If your real situation is different from the example code, then you can mention the aspect explicitly in another include statement. – kriegaex Dec 20 '21 at 07:56
  • OK, I updated my answer with a more generic solution for `aop.xml`. Try that and see if it makes you happy enough to accept and upvote my answer. – kriegaex Dec 20 '21 at 07:59
  • But successfully intercepted in [MVCE](https://github.com/avenue68/repository-for-sharing/blob/master/demo/src/main/resources/META-INF/aop.xml) – avenue68 Dec 20 '21 at 07:59
  • Thank you for updating answer! I strongly want to upvote your answer but I seem not to have the privilege to do that yet, sorry... I'm searching questions I can answer to get the privilege. I found `NullPointerException` is thrown if the aspects have @Around advice! Any ideas? I pushed this change to [MVCE](https://github.com/avenue68/repository-for-sharing/blob/master/demo/src/main/java/com/example/MyAspect.java). – avenue68 Dec 20 '21 at 08:27
  • No problem, I did not notice your low reputation score. Just accept the answer (click the grey checkmark, making it green). Otherwise it will still be listed as unanswered. – kriegaex Dec 20 '21 at 08:41
  • I created another PR and also answered your follow-up question in another update, see above. Please refrain from changing the original code again and asking more follow-up questions here. If you have more questions, please create a new question. – kriegaex Dec 20 '21 at 08:56
  • I checked your PR and it worked well! I really appreciate your detail and quick answer, thanks! And also appreciate telling SO's manner, I'll be careful not to do them. – avenue68 Dec 20 '21 at 09:05