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

Java 在订阅方法之前结束方面调用,java,reactive-programming,aop,aspectj,aspect,Java,Reactive Programming,Aop,Aspectj,Aspect,我有一个方法,其中包含反应式代码(RxJava) 我有一个方面围绕着它 设置很好,它按如下方式进行打印 但它发生在该方法被订阅之前 有没有一种方法可以将其设置为只有在订阅了该方法之后,特性才会启动 我的方面课 @Aspect public class AspectClass { @Around("@annotation(someLogger) && execution(* *(..))") public Object getM

我有一个方法,其中包含反应式代码(RxJava)

我有一个方面围绕着它

设置很好,它按如下方式进行打印

但它发生在该方法被订阅之前

有没有一种方法可以将其设置为只有在订阅了该方法之后,特性才会启动

我的方面课

@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()));

}
我在等下面的订单

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
这可能吗

这是我发现的与此相关的最接近的问题。

但这个问题是基于春天的。答案中还有一行:

您确实需要让方面了解异步情况

听起来这是我需要做的,但我该怎么做呢?谢谢你的建议

---建议后更新---

注意,我使用的是AspectJ而不是Spring

这不起作用,因为订阅前变量PROCEND为null

因此,我添加了一个空检查。但我们只会进入这里一次

因此继续。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");

}
有什么问题? 问题不在于方面,而在于您对如何以及何时执行您试图拦截的代码的理解。方面完全按照您的要求执行:它在执行带注释的方法之前和之后记录一些内容。到目前为止,一切顺利

你应该怎么做 您想要截获的是在
add
方法中注册的异步回调,该方法配置行为以供以后执行。如果您想这样做,您应该截取提供给
doOnSubscribe
doOnEach
的代码,并在
@之前
@之后
通知,这取决于您希望何时打印信息(在您的示例日志中,您似乎更喜欢在之后)

A) 如果使用Spring AOP 为了做到这一点,您不能将lambdas与springaop结合使用,因为springaop只能截取Spring组件中的代码。因此,您必须将这两个lambda提取到类中(本质上它们只是匿名类),生成这些类springbean,然后拦截它们的方法。我希望您知道,lambda基本上是一个匿名类,它用一个方法实现一个接口(实际上不是在JVM字节码中,只是为了简单理解)。因此,如果您从
Consumer
Observer
或任何回调实现的RxJava子类中创建单独的Spring组件,您可以通过Spring AOP拦截它们。但这意味着要修改您的代码,以适应并促进AOP的使用。您的代码也会变得不那么简洁和富有表现力。但是你问了这个问题,我只是在回答

B) 如果使用AspectJ 如果您从SpringAOP切换到AspectJ,或者已经在使用AspectJ,那么您可以尝试直接针对lambda,但这也很棘手。我必须写一个很长的答案来解释原因,你可以阅读并了解更多信息


如果您将lambda转换为经典的匿名子类(像IntelliJ这样的好IDE思想应该可以帮助您在需要时通过两次鼠标点击自动完成这一工作),那么您就可以通过AspectJ进行拦截。但同样,您的编程风格也要适应AOP的使用,因为目前AspectJ对lambdas没有明确的支持。

谢谢。根据“你应该做什么”下的建议更新了问题,并尝试了。在原始问题中,你使用了一个
@环绕的
建议。这是
ProceedingJoinPoint jp
jp.procedure()
fit的位置。编辑后的第二个方面在
建议之前显示一个
@。在这里,您必须使用一个普通的
连接点
,并且永远不要调用
继续()
,因为除非您在通知中抛出异常,否则目标方法将在之后自动执行。你试图施展并继续表演表明你并不理解这一点。你真的应该读一些文档,而不是仅仅是因为试错而被窃取。@kriegaex确实读过这些文档。这就是为什么我在@before中使用JoinPoint。了解您不在此处使用ProceedingJoinPoint的观点。在正常情况下,上述示例将很好地工作。我一直无法理解如何在这里使用before方法进行doOnSubscribe。文档中没有涉及到这一点,至少我看不到。然后再次阅读文档。尝试将正常连接点从before通知强制转换为预期在around通知中使用的继续连接点是没有意义的。调用
procedure()
更疯狂。我100%确信,在任何文档、教程或示例代码中,您都不会发现这一点。这完全是错误的。在您担心
doOnsubscribe()
之前,先学习如何使用AspectJ。这就是我的观点。@kriegaex如果不是因为我担心doOnsubscribe()试图看看是否有办法让它工作的话,以前绝对不会在@中处理JoinPoint。无论如何,谢谢你的回复,我不明白。如果您只想捕获结果并记录一些内容,请在返回后使用
>,而不是在返回前使用
>。您希望在方法返回之后而不是之前执行某些操作。如果要更改返回值和/或在原始方法之前+之后执行某些操作,请使用
@Around
。如果您不知道,您就不能阅读任何文档。我真的很想帮你。第三次也是最后一次,您的问题与
doOnSubscribe()
无关。你需要练习和理解AOP。
@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");

}
@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()));
}