Почему ты делаешь простую вещь сложной? Пользователь JB Nizet уже сказал вам:
Единственный способ узнать тип возвращаемого поставщиком объекта - позвонить поставщику.
Ваш собственный код делает это очень надуманным способом (после того, как я исправил его, чтобы он компилировался, потому что он глючит), используя рефлексию. Просто используйте привязку параметра аргумента AspectJ через args()
и сделайте его безопасным для типов. Также используйте @Before
вместо @Around
, если вы просто хотите записать возвращаемое значение Supplier
и никак не повлиять на выполнение метода. Таким образом, вы можете избежать вызова ProceedingJoinPoint.proceed()
, чего-то необходимого, но полностью отсутствующего в вашем примере кода «решения».
Как насчет этого маленького 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());
}
}
Журнал консоли при запуске B.main(..)
:
execution(void de.scrum_master.app.A.foo(Supplier)) -> de.scrum_master.app.MyCustomClass@66a29884
Это то же, что пытается сделать ваш аспект, только более аккуратно. Я думаю, что он определенно более читабелен.
Предостережение: Пожалуйста, дважды подумайте, прежде чем звонить get()
поставщику, если у поставщика есть побочный эффект или его дорого рассчитать. Я знаю, что чистые функции (т.е. код, реализующий функциональные интерфейсы на Java говорят) никогда не должны иметь побочных эффектов, но если они написаны в плохом стиле, они легко могут. Так что будьте осторожны.
Обновление: Говоря об оговорке с побочным эффектом, позвольте мне показать вам кое-что. Просто немного расширьте код приложения, чтобы реально оценить поставщика и (опционально) вернуть его результат:
package de.scrum_master.app;
import java.util.function.Supplier;
public class A {
public Object foo(Supplier<?> msg) {
return msg.get();
}
}
А теперь также давайте расширим аспект, чтобы фактически запускать ведение журнала всякий раз, когда get()
поставщика метод называется:
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);
}
}
Теперь журнал консоли будет:
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
Вы можете видеть, как Supplier.get()
вызывается дважды, возвращая два разных объекта MyCustomClass
, а именно MyCustomClass@66a29884
и MyCustomClass@4769b07b
? Это связано с тем, что и приложение, и первая консультация по аспекту звонят get()
. Последний на самом деле не регистрирует тот же объект, что и объект, созданный приложением, поэтому даже без дополнительных побочных эффектов вы регистрируете неправильную вещь и выполняете метод поставщика дважды, а не один раз.
Так что давайте очистим это больше не вызывая get()
из первого метода извещения:
@Before("execution(* *(*)) && args(supplier)")
public void methodCallWithSupplierArgument(JoinPoint thisJoinPoint, Supplier<?> supplier) throws Exception {
System.out.println(thisJoinPoint + " -> " + supplier); // no 'get()' call anymore
}
Теперь журнал становится чистым:
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
Другое преимущество состоит в том, что теперь результат get()
получает регистрируется всякий раз, когда метод действительно вызывается (может быть выполнен синхронно, асинхронно, многократно или никогда), а не тогда, когда аспект выполняет его избыточно.
PS: Если вам интересно, почему я так тщательно не выполняю get()
только для целей регистрации, просто представьте, что лямбда открывает соединение с базой данных, создает файл 4 ГБ, загружает видео 4K с продолжительностью 90 минут или что-то еще. Это будет сделано дважды, чтобы вы могли записать это.