I have the following AspectJ example that I've done as a "hello world" style proof of concept. The advising code in the StyleAspect
seems to execute twice even though the actual code in SomeClass
only executes once (as required).
Here's the code:
Firstly, an annotation called WithStyle:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface WithStyle {
}
Then, an aspect that intercepts any code with the @WithStyle annotation
@Aspect
public class StyleAspect {
@Around("@annotation(WithStyle)")
public Object doItWithStyle(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Doing it in style...");
Object result = pjp.proceed();
System.out.println("Done");
return result;
}
}
and finally, some code with the annotation
public class SomeClass {
@WithStyle
public void doIt() {
System.out.println("I'm doing it....");
}
}
When I run this, I get the following output:
--- exec-maven-plugin:1.2.1:exec (default-cli) @ AspectJTest ---
Doing it in style...
Doing it in style...
I'm doing it....
Done
Done
So it seems as if while the code itself only executes once, the code in the aspect is getting executed twice.
Here's the calling code:
public class Main {
public static void main(String[] args) {
SomeClass someClass = new SomeClass();
someClass.doIt();
}
}
and for completeness, I'm including the pom with the AspectJ plugin config
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ie.philb</groupId>
<artifactId>AspectJTest</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.java.target>1.8</project.build.java.target>
</properties>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.11</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal> <!-- use this goal to weave all your main classes -->
<goal>test-compile</goal> <!-- use this goal to weave all your test classes -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Using only annotations creates another problem that we don't need to think about while using patterns; It will make our advice run twice(or more), because the annotation pointcut don't specify if it should be run during execution or initialization.
AspectJ supports three kinds of advice. The kind of advice determines how it interacts with the join points it is defined over. Thus AspectJ divides advice into that which runs before its join points, that which runs after its join points, and that which runs in place of (or "around") its join points.
Which advice is executed once a joint point finishes? Explanation: An after advice is executed after a join point finishes, whenever it returns a result or throws an exception abnormally.
Pointcut: Pointcut is expressions that are matched with join points to determine whether advice needs to be executed or not. Pointcut uses different kinds of expressions that are matched with the join points and Spring framework uses the AspectJ pointcut expression language.
Your around()
advice is intercepting both the call
and execution
join points of the method annotated with @WithStyle
(i.e., doIt()
). If you add a System.out.println(pjp);
to your aspect:
@Aspect
public class StyleAspect {
@Around("@annotation(WithStyle) ")
public Object doItWithStyle(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(pjp);
System.out.println("Doing it in style...");
Object result;
try{
result = pjp.proceed();
}
finally{
System.out.println("Done");
}
return result;
}
}
you would get the following output:
call(public void SomeClass.doIt()) <----
Doing it in style...
execution(public void SomeClass.doIt()) <----
Doing it in style...
I'm doing it....
Done
Done
You can clearly see that the join points call
and execution
of method SomeClass.doIt()
are being intercepted by the around
advice doItWithStyle
.
From the interception of the call
, the around
advice is weaving the code as follows:
// around advice code before the pjp.proceed();
someClass.doIt(); // during the pjp.proceed();
// around advice code after the pjp.proceed();
consequently:
System.out.println("Doing it in style...");.
someClass.doIt();
System.out.println("Done");
From the execution:
@WithStyle
public void doIt() {
// around advice code before the pjp.proceed();
System.out.println("I'm doing it....");
// around advice code after the pjp.proceed();
}
consequently:
@WithStyle
public void doIt() {
System.out.println("Doing it in style...");
System.out.println("I'm doing it....");
System.out.println("Done");
}
resulting in the output:
Doing it in style...
Doing it in style...
I'm doing it....
Done
Done
Now, if you want to avoid the around
advice from intercepting both the call
and the execution
of the method doIt()
. You need to further restrict the join points intercepted by your around
advice. To just intercept the method call
, you can do:
@Around("@annotation(WithStyle) && call(* *(..))")
for the method execution
:
@Around("@annotation(WithStyle) && execution(* *(..))")
You can further restrict the join points being intercepted based on the number of arguments of the method, its returning type, name, and so on, by tuning the signature of the call
or execution
pointcuts.
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