在订阅方法之前,方面调用结束 A如果您使用Spring AOP B如果您使用AspectJ

问题描述

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

我周围有一个Aspect @around。

设置很好,它执行以下打印输出

但是它发生在该方法尚未订阅之前。

有没有一种方法可以将其设置为仅在订阅方法后才启动Aspect?

我的方面类

@H_404_12@@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; } }

自定义注释

@H_404_12@@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface SomeLogger { String name() default ""; }

使用方面进行注释的方法

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

如前所述,即使在订阅之前也要打印出方面类中的打印行。

这是当前打印错误的顺序。

@H_404_12@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

我期望以下订单。

@H_404_12@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。

这不起作用,因为变量cos变量在订阅前为null。

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

因此procedure.doOnSubscribe()永远不会发生。

@H_404_12@@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编译器错误

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

解决方法

出什么问题了?

问题不在于方面,而是您对试图拦截的代码的执行方式和时间的理解。该方面完全按照您的指示进行操作:它在执行带注释的方法之前和之后记录一些内容。到目前为止,一切都很好。

您应该怎么做

您要拦截的是您在def sort_by_ext(files): return sorted(files,key=lambda x:(bool(i:=x.rfind('.')),x[i+1:],x[:i])) 方法中注册的异步回调,这些异步回调配置了行为以便以后执行。如果要执行此操作,您应该根据adddoOnSubscribe的建议,截取提供给doOnEach@Before的代码,具体取决于您希望何时打印信息(在您似乎更喜欢的示例日志)。

A)如果您使用Spring AOP

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

B)如果您使用AspectJ

如果您从Spring AOP切换到AspectJ或已经使用过AspectJ,则可以尝试直接定位lambda,但这也很棘手。我将不得不写一个很长的答案来解释为什么,您可以阅读this answerAspectJ issue #471347以获得更多信息。

如果将lambda转换为经典的匿名子类会变得更加容易(像IntelliJ IDEA这样的好IDE可以在您要求两次单击时帮助您自动完成此操作),然后您可以通过AspectJ进行拦截。但是,您又要根据AOP的使用来适应编程风格,因为AspectJ目前还没有对lambda的明确支持。