gpt4 book ai didi

java - 方面调用在订阅方法之前结束

转载 作者:行者123 更新时间:2023-12-04 13:32:26 24 4
gpt4 key购买 nike

我有一个方法,其中包含响应式代码(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 的。该答案中还有一行:

You do need to make the aspect aware of the asynchronous situation.


听起来这就是我需要做的,但我该怎么做呢?感谢您的任何建议。
--- 建议后更新 ---
请注意,我使用的是 AspectJ 而不是 Spring。
这不起作用 cos 变量在订阅前为 null。
因此我添加了一个空检查。但我们只会进入这里一次。
因此,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()));
}

最佳答案

问题是什么?
问题不在于方面,而在于您对尝试拦截的代码如何以及何时执行的理解。该方面完全按照您的要求执行:它在执行带注释的方法之前和之后记录一些内容。到现在为止还挺好。
你应该做什么
你要拦截的是你在add中注册的异步回调方法配置行为以供以后执行。如果你想这样做,你应该截取提供给 doOnSubscribe 的代码。和 doOnEach@Before@After建议,取决于您希望何时打印信息(在您的示例日志中,您似乎更喜欢之后)。
A) 如果你使用 Spring AOP
为此,您不能将 lambda 与 Spring AOP 结合使用,因为 Spring AOP 只能拦截 Spring 组件中的代码。因此,您必须将两个 lambda 表达式提取到类中(本质上它们是匿名的),使这些类成为 Spring bean,然后拦截它们的方法。我希望你知道 lambda 基本上是一个匿名类,它实现了一个带有单个方法的接口(interface)(不是真正在 JVM 字节码内部,而是为了简单理解)。因此,如果您从 Consumer 的 RxJava 子类创建单独的 Spring 组件, Observer或者无论您的回调实现了什么,您都可以通过 Spring AOP 拦截它们。但这意味着修改您的代码以适应和促进 AOP 的使用。您的代码也会变得不那么简洁和富有表现力。但是你问这个问题,我只是在回答。
B) 如果你使用 AspectJ
如果你从 Spring AOP 切换到 AspectJ 或者已经在使用 AspectJ,你可以尝试直接针对 lambdas,但这也很棘手。我将不得不写一个很长的答案来解释原因,你可以阅读this answerAspectJ issue #471347想要查询更多的信息。
如果您将 lambda 转换为经典的匿名子类(像 IntelliJ IDEA 这样的优秀 IDE 应该可以帮助您在您要求时单击两次鼠标自动为您执行此操作),这会变得更容易,然后您可以通过 AspectJ 进行拦截。但同样,您将自己的编程风格迎合 AOP 的使用,因为目前 AspectJ 没有明确支持 lambdas。

关于java - 方面调用在订阅方法之前结束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64199679/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com