Why are you making a simple thing complicated? User JB Nizet already told you:
The only way to get the type of the object returned by the supplier is to call the supplier.
Your own code does that in a very contrived way (after I fixed it to make it compile because it is buggy), using reflection. Just use AspectJ argument parameter binding via args()
and make it type-safe. Also use @Before
instead of @Around
if you just want to log the Supplier
's return value and not otherwise influence method execution. This way you can avoid calling ProceedingJoinPoint.proceed()
, something necessary but completely missing in your "solution" sample code.
How about this little MCVE?
package de.scrum_master.app;
public class MyCustomClass {}
package de.scrum_master.app;
import java.util.function.Supplier;
public class A {
public void foo(Supplier<?> msg) {}
}
package de.scrum_master.app;
public class B {
public void bar() {
A a = new A();
a.foo(() -> new MyCustomClass());
}
public static void main(String[] args) {
new B().bar();
}
}
package de.scrum_master.aspect;
import java.util.function.Supplier;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspect {
@Before("execution(* *(*)) && args(supplier)")
public void methodCallWithSupplierArgument(JoinPoint thisJoinPoint, Supplier<?> supplier) throws Exception {
System.out.println(thisJoinPoint + " -> " + supplier.get());
}
}
The console log when running B.main(..)
:
execution(void de.scrum_master.app.A.foo(Supplier)) -> de.scrum_master.app.MyCustomClass@66a29884
This is the same as your aspect is trying to do, just more cleanly. I think it definitely is more readable, too.
Caveat: Please think twice before calling get()
on the supplier if the supplier has a side effect or is expensive to calculate. I know that pure functions (i.e. code implementing functional interfaces in Java speak) should never have any side effects, but if coded in a bad style they easily can. So be careful.
Update: Talking about the caveat with the side effect, let me show you something. Just extend the application code a little bit so as to actually evaluate the supplier and (optionally) return its result:
package de.scrum_master.app;
import java.util.function.Supplier;
public class A {
public Object foo(Supplier<?> msg) {
return msg.get();
}
}
And now also let us extend the aspect to actually trigger logging whenever a supplier's get()
method is called:
package de.scrum_master.aspect;
import java.util.function.Supplier;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class MyAspect {
@Before("execution(* *(*)) && args(supplier)")
public void methodCallWithSupplierArgument(JoinPoint thisJoinPoint, Supplier<?> supplier) throws Exception {
System.out.println(thisJoinPoint + " -> " + supplier.get());
}
@AfterReturning(pointcut = "call(public * java.util.function.Supplier+.get())", returning = "result")
public void supplierEvaluated(JoinPoint thisJoinPoint, Object result) throws Exception {
System.out.println(thisJoinPoint + " -> " + result);
}
}
Now the console log will be:
call(Object java.util.function.Supplier.get()) -> de.scrum_master.app.MyCustomClass@66a29884
execution(Object de.scrum_master.app.A.foo(Supplier)) -> de.scrum_master.app.MyCustomClass@66a29884
call(Object java.util.function.Supplier.get()) -> de.scrum_master.app.MyCustomClass@4769b07b
Can you see how Supplier.get()
is called twice, returning two different MyCustomClass
objects, namely MyCustomClass@66a29884
and MyCustomClass@4769b07b
? This is because both the application and the first aspect advice call get()
. The latter does not really log the same object as the one created by the application, so even without further side effects you are logging the wrong thing and executing a supplier method twice instead of just once.
So let us clean this up by not calling get()
anymore from the first advice method:
@Before("execution(* *(*)) && args(supplier)")
public void methodCallWithSupplierArgument(JoinPoint thisJoinPoint, Supplier<?> supplier) throws Exception {
System.out.println(thisJoinPoint + " -> " + supplier); // no 'get()' call anymore
}
Now the log becomes clean:
execution(Object de.scrum_master.app.A.foo(Supplier)) -> de.scrum_master.app.B$$Lambda$1/1349393271@66a29884
call(Object java.util.function.Supplier.get()) -> de.scrum_master.app.MyCustomClass@4769b07b
Another advantage is that now the get()
result gets logged whenever the method is really called (could be done synchronously, asynchronously, multiple times or never) and not when the aspect executes it redundantly.
P.S.: If you are wondering why I am so meticulous about not executing get()
just for logging purposes, just imagine that the lambda opens a database connection, creates a 4 GB file, downloads a 4K video with duration 90 minutes or whatever. It would be done twice, just so as to let you log it.