For the life of me, I can't figure out how to get some AspectJ advice to run.
build.gradle
:
plugins {
id "io.freefair.aspectj" version "8.3"
}
dependencies {
// https://mvnrepository.com/artifact/org.aspectj/aspectjweaver
implementation group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.20.1'
// https://mvnrepository.com/artifact/org.aspectj/aspectjrt
implementation group: 'org.aspectj', name: 'aspectjrt', version: '1.9.20.1'
}
LogExecutionTime.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecutionTime {}
LogExecutionTimeAspect.java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class LogExecutionTimeAspect {
@Around("@annotation(LogExecutionTime)")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = pjp.proceed();
long executionTime = System.currentTimeMillis() - start;
System.out.println(pjp.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
TestClass.java
public class TestClass {
@LogExecutionTime
public String hello() {
return "hello";
}
}
I've called new TestClass().hello()
in both a unit test (JUnit 5) and when running our application locally (Appengine Web App). Everything compiles and runs, but the execution time isn't getting printed out. I've tried debugging and putting a break point inside the LogExecutionTimeAspect::around
method, but it was never reached.
I feel like it shouldn't be this complicated, but I can't seem to figure it out.
I have a basic working project here for anyone interested: https://github.com/MuffinTheMan/gradle-aspectj
For me, this works. Did you really forget the java
plugin, or did you post an incomplete Gradle build file? What I cannot know is if your annotation is in the exact same package as the aspect, otherwise you would need a fully qualified class name in the @annotation()
pointcut.
plugins {
id 'java'
id "io.freefair.aspectj" version "8.3"
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
implementation 'org.aspectj:aspectjrt:1.9.20.1'
}
package de.scrum_master.stackoverflow.q77174865;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecutionTime {}
package de.scrum_master.stackoverflow.q77174865;
public class TestClass {
@LogExecutionTime
public String hello() {
return "hello";
}
public static void main(String[] args) {
new TestClass().hello();
}
}
package de.scrum_master.stackoverflow.q77174865;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class LogExecutionTimeAspect {
@Around("@annotation(LogExecutionTime)")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = pjp.proceed();
long executionTime = System.currentTimeMillis() - start;
System.out.println(pjp.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
Running from my IDE IntelliJ IDEA, the console log looks as follows:
13:24:24: Executing ':TestClass.main()'...
> Task :compileJava NO-SOURCE
> Task :compileAspectj
> Task :processResources NO-SOURCE
> Task :classes
> Task :TestClass.main()
String de.scrum_master.stackoverflow.q77174865.TestClass.hello() executed in 0ms
String de.scrum_master.stackoverflow.q77174865.TestClass.hello() executed in 2ms
(...)
Of course, you see two log messages, because your pointcut captures both call()
and execution()
joinpoints. You can fix that as follows:
package de.scrum_master.stackoverflow.q77174865;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class LogExecutionTimeAspect {
@Around("@annotation(LogExecutionTime) && execution(* *(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
System.out.println(pjp.getSignature() + " executed in " + (System.currentTimeMillis() - start) + "ms");
}
}
}
One thing you maybe forgot is that for Freefair, the default source directory is src/man/aspectj
. For me, it looks like this:
The default will probably change to src/main/java
in version 8.4, see GitHub issue #881.
Update 2023-10-10: Freefair 8.4 has been published and now defaults to src/main/java
, i.e. it works as expected with a standard directory layout, just like with AspectJ Maven.