I want to intercept object creation for a certain class using AspectJ, and insert some other object.
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AnnotationAspect {
@Around("execution(ExampleClass.new())")
public Object aroundObjectCreation(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Intercepting object creation");
return null;
}
}
When I create an object in the main program:
public class Test {
public static void main(String[] args) {
ExampleClass e = new ExampleClass();
System.out.println(e);
}
}
instead of the expected null value, I get the following output:
Intercepting object creation
com.example.ExampleClass@19bb07ed
How is this possible? The program never reached the real ExampleClass constructor, yet somehow the object is created.
That is an interesting question, so I want to answer it a bit more extensively. As a reference for the additional pointcut types I am going to mention, please use this overview from the AspectJ Programming Guide.
For constructor interception, there are 4 relevant pointcut types:
call
: marks the place in the code where the constructor is calledexecution
: marks the place in the code where the constructor is executedpreinitialization
: encompasses the entry of the first-called constructor to the call to the super constructorinitialization
: encompasses the return from the super constructor call to the return of the first-called constructorPlease note that while for the former two, an around advice is possible, for the latter two only before/after advice types are permitted, i.e. you cannot stop them from being executed by not proceeding. I.e., you cannot just return null from a constructor call, because the JVM is not designed that way.
First, let us extend the sample code, so we can see more:
package de.scrum_master.stackoverflow.q77438354;
public class BaseClass {
final int id;
public BaseClass(int id) {
System.out.println("In BaseClass constructor");
this.id = id;
}
}
package de.scrum_master.stackoverflow.q77438354;
public class ExampleClass extends BaseClass {
final String name;
public ExampleClass() {
super(11);
System.out.println("In ExampleClass constructor");
name = "Jane Doe";
}
@Override
public String toString() {
return "ExampleClass{id=" + id + ", name=" + name + "}";
}
}
package de.scrum_master.stackoverflow.q77438354;
public class Application {
public static void main(String[] args) {
System.out.println(new ExampleClass());
}
}
package de.scrum_master.stackoverflow.q77438354;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class ConstructorInterceptorAspect {
@Around("execution(ExampleClass.new())")
public Object aroundObjectCreation(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("[B] " + joinPoint + " -> " + joinPoint.getTarget());
try {
System.out.println("Intercepting object creation");
return null;
} finally {
System.out.println("[A] " + joinPoint + " -> " + joinPoint.getTarget());
}
}
@Before("preinitialization(ExampleClass.new()) || initialization(ExampleClass.new()) || call(ExampleClass.new())")
public void beforeInit(JoinPoint joinPoint) {
System.out.println("[B] " + joinPoint + " -> " + joinPoint.getTarget());
}
@After("preinitialization(ExampleClass.new()) || initialization(ExampleClass.new()) || call(ExampleClass.new())")
public void afterInit(JoinPoint joinPoint) {
System.out.println("[A] " + joinPoint + " -> " + joinPoint.getTarget());
}
}
The console log looks as follows:
[B] call(de.scrum_master.stackoverflow.q77438354.ExampleClass()) -> null
[B] preinitialization(de.scrum_master.stackoverflow.q77438354.ExampleClass()) -> null
[A] preinitialization(de.scrum_master.stackoverflow.q77438354.ExampleClass()) -> null
In BaseClass constructor
[B] initialization(de.scrum_master.stackoverflow.q77438354.ExampleClass()) -> ExampleClass{id=11, name=null}
[B] execution(de.scrum_master.stackoverflow.q77438354.ExampleClass()) -> ExampleClass{id=11, name=null}
Intercepting object creation
[A] execution(de.scrum_master.stackoverflow.q77438354.ExampleClass()) -> ExampleClass{id=11, name=null}
[A] initialization(de.scrum_master.stackoverflow.q77438354.ExampleClass()) -> ExampleClass{id=11, name=null}
[A] call(de.scrum_master.stackoverflow.q77438354.ExampleClass()) -> null
ExampleClass{id=11, name=null}
Please note:
execution
is even reached, preinitialization
has finished already and initialization
wraps execution
, i.e. it starts before execution
and ends later.ExampleClass{id=11, name=null}
at that time, i.e. the super class field was initialised already, while the subclass field has no value yet.null
, i.e. during pre-initialisation there is not even an instance constructed yet.execution
advice successfully blocks field initialisation in the intercepted constructor, i.e. the field value remains name=null
. BTW, the same way you could also prohibit field initialisation in the super class constructor, if you would change your around execution pointcut correspondingly.Actually, your log output "Intercepting object creation" is not entirely correct. You are just intercepting constructor execution, but there is no way to stop object creation and return null instead. You could only throw an exception to stop the object from being created.
The reason that you can return null
in the around call
advice is that the object has already been created, i.e. execution has finished, but you simply replace the created value by null.