【发布时间】:2020-10-04 20:41:45
【问题描述】:
我有一个方法,其中包含响应式代码 (RxJava)。
我有一个 Aspect @around 环绕它。
设置很好,打印如下。
但它发生在甚至订阅该方法之前。
有没有一种方法可以设置它,使得 Aspect 仅在方法被订阅后才启动?
我的方面类
@Aspect
public class AspectClass {
@Around("@annotation(someLogger) && execution(* *(..))")
public Object getMockedData(ProceedingJoinPoint pjp, SomeLogger someLogger) throws Throwable {
System.out.println("from aspect start: " + Thread.currentThread().getName());
Object actualResponse = pjp.proceed(methodArguments);
System.out.println("from aspect close: " + Thread.currentThread().getName());
return actualResponse;
}
}
自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeLogger {
String name() default "";
}
被注释以使用方面的方法。
@SomeLogger(name = "name")
public Single<Response> add(Request request) {
return sample(request)
.flatMapObservable(resultSet -> Observable.from(resultSet.getRows()))
.map(row -> Response.builder()
.sampleTimestamp(localDateTime(row.getInstant("sample")))
.build()
)
.first()
.toSingle()
.doOnSubscribe(() -> System.out.println("method is subbed: add " + Thread.currentThread().getName()))
.doOnEach(n -> System.out.println("method ends and doOnEach: add " + Thread.currentThread().getName()));
}
如前所述,方面类中的打印行甚至在订阅之前就被打印出来,这是错误的。
因此这是当前错误的打印顺序。
from aspect start: eventloop-thread-3
from aspect close: eventloop-thread-3
method is subbed: add eventloop-thread-3
method ends and doOnEach: add eventloop-thread-3
我期待以下订单。
method is subbed: add eventloop-thread-3
from aspect start: eventloop-thread-3
method ends and doOnEach: add eventloop-thread-3
from aspect close: eventloop-thread-3
这可能吗?
这是我发现的最接近的问题。 Writing Aspects For Reactive Pipelines
但这个问题是基于 Spring 的。该答案中还有一行:
您确实需要让方面了解异步情况。
听起来这是我需要做的,但我该怎么做呢?感谢您的建议。
--- 建议后更新 ---
请注意,我使用的是 AspectJ 而不是 Spring。
这不起作用,因为订阅前变量proceed为空。
因此我添加了一个空检查。但我们只打算进入这里一次。
因此,proceed.doOnSubscribe() 永远不会发生。
@Before("@annotation(someLogger) && execution(* *(..))")
public void before(JoinPoint jp, SomeLogger someLogger) throws Throwable {
Object[] args = jp.getArgs();
Single<?> proceed = ((Single<?>) ((ProceedingJoinPoint) jp).proceed(args));
if(proceed != null) {
proceed.doOnSubscribe(() -> System.out.println("doOnSubscribe in before"));
}
// this will print before a subscription
// as expected before which is not what I want.
System.out.println("inside before");
}
进一步尝试:
至少在理论上期望这会起作用。但会引发 AJC 编译器错误。
@Around("@annotation(someLogger) && execution(* *(..))")
public Object around(ProceedingJoinPoint pjp, SomeLogger someLogger) {
// return pjp.proceed(methodArguments); // compiles fine if no operations on method
return pjp.proceed(methodArguments)
.doOnSubscribe(() -> System.out.println("method is subbed: add " + Thread.currentThread().getName()))
.doOnEach(n -> System.out.println("method ends and doOnEach: add " + Thread.currentThread().getName()));
}
【问题讨论】:
-
在您最初的问题中,您使用了
@Around建议。这是ProceedingJoinPoint jp和jp.proceed()适合的地方。编辑后的第二个方面显示@Before建议。在那里你必须使用普通的JoinPoint,并且你永远不会调用proceed(),因为除非你在通知中抛出异常,否则目标方法将在之后自动执行。您尝试投射并继续表明您不理解这一点。你真的应该阅读一些文档,而不是通过反复试验来破解。 -
@kriegaex 确实阅读了文件。这就是为什么我之前使用 JoinPoint for @ 的原因。了解您在此处不使用 ProceedingJoinPoint 的观点。上述示例在正常情况下可以正常工作。我一直不明白如何在这里的 before 方法中执行 doOnSubscribe 。这不在文档中,至少我看不到。
-
然后再次阅读文档。尝试将普通连接点从之前的建议转换为预期将在周围建议中使用的正在进行的连接点是没有意义的。打电话给
proceed()更疯狂。我 100% 确定您从未在任何文档、教程或示例代码中发现过这一点。这是完全错误的。在您担心doOnsubscribe()之前,先了解如何使用 AspectJ。这就是我的观点。 -
@kriegaex 如果不是因为我担心 doOnsubscribe() 试图查看是否有解决办法让它工作,我永远不会在 @Before 中为 ProceedingJoinPoint 烦恼。无论如何感谢您的回复。
-
我不明白。如果您只想捕获结果并记录一些内容,请使用
@AfterReturning,而不是@Before。你想在方法返回之后之后做一些事情,而不是之前。如果要更改返回值和/或在原始方法之后的 + 之前执行某些操作,请使用@Around。如果您不知道,您将无法阅读任何文档。我真的很想在这里提供帮助。第三次也是最后一次,您的问题与doOnSubscribe()无关。你需要练习和理解 AOP。
标签: java reactive-programming aop aspectj aspect