问题描述
我周围有一个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]))
方法中注册的异步回调,这些异步回调配置了行为以便以后执行。如果要执行此操作,您应该根据add
或doOnSubscribe
的建议,截取提供给doOnEach
和@Before
的代码,具体取决于您希望何时打印信息(在您似乎更喜欢的示例日志)。
A)如果您使用Spring AOP
为此,您不能将lambda与Spring AOP结合使用,因为Spring AOP只能拦截Spring组件中的代码。因此,您必须将两个lambda都提取到类中(本质上它们是匿名类),使这些类成为Spring Bean,然后拦截它们的方法。我希望您知道lambda基本上是用单个方法实现接口的匿名类(实际上不是在JVM字节码内部,而是为了简单理解)。因此,如果您从@After
,Consumer
或您的回调实现的RxJava子类创建单独的Spring组件,则可以通过Spring AOP拦截它们。但这将意味着修改您的代码,以适应和促进AOP的使用。您的代码也不会那么简洁和富有表现力。但是你问了一个问题,我只是在回答。
B)如果您使用AspectJ
如果您从Spring AOP切换到AspectJ或已经使用过AspectJ,则可以尝试直接定位lambda,但这也很棘手。我将不得不写一个很长的答案来解释为什么,您可以阅读this answer和AspectJ issue #471347以获得更多信息。
如果将lambda转换为经典的匿名子类会变得更加容易(像IntelliJ IDEA这样的好IDE可以在您要求两次单击时帮助您自动完成此操作),然后您可以通过AspectJ进行拦截。但是,您又要根据AOP的使用来适应编程风格,因为AspectJ目前还没有对lambda的明确支持。