在使用Spring/Java和面向方面的编程编写代码时,我面临一个问题。在服务类中,我有使用@Retryable的retry方法和使用@Recover的恢复方法。
这两种方法中的每一种都附加到各个方面。在triggerJob类中,TestProcessService中的Retryable“afterTestTriggerJobsAdvice”附加到这些方法中- beforeTestTriggerJobsAdvice、afterTestTriggerJobsAdvice、onErrorTestTriggerJobsAdvice。他们都工作得很好,并在正确的时间被触发。
问题陈述:-- TestProcessService中的恢复方法--在TestAspect类中附加到这些方法- beforeRecoveryTestJobsAdvice、onErrorRecoveryTestTriggerJobsAdvice和afterRecoveryTestTriggerJobsAdvice中。
但是,一旦代码到达内部的恢复方法,所有这些方面方法都不会被调用。
以下是代码:
调度程序类(触发TEST_MyProcessService类内的方法)
@Slf4j
@Component
public class TEST_ScheduledProcessPoller {
private final TEST_MyProcessService MyProcessService;
private final MyServicesConfiguration MyServicesConfiguration;
public TEST_ScheduledProcessPoller(TEST_MyProcessService MyProcessService,
MyServicesConfiguration MyServicesConfiguration) {
this.MyProcessService = MyProcessService;
this.MyServicesConfiguration = MyServicesConfiguration;
}
@Scheduled(cron = "0 0/2 * * * *")
public void scheduleTaskWithFixedDelay() {
try {
log.info("scheduleTaskWithFixedDelay");
this.triggerMyJobs(true);
} catch (Exception e) {
log.error(e.getMessage());
}
}
protected void triggerMyJobs(boolean isDaily) throws Exception {
log.info("triggerMyJobs");
MyServiceType serviceType = this.MyServicesConfiguration.getMy();
this.MyProcessService.triggerJob(serviceType, isDaily, 1L);
}
}服务类:
@Slf4j
@Service
public class TEST_MyProcessService {
@Retryable(maxAttemptsExpression = "${api.retry.limit}", backoff = @Backoff(delayExpression = "${api.retry.max-interval}"))
public void triggerJob(MyServiceType MyServiceType, boolean isDaily, long eventId) {
// Some code here that can throw exceptions.
log.info("triggerJob");
throw new RuntimeException("triggerJob");
}
@Recover
public void recover(MyServiceType MyServiceType, boolean isDaily, long eventId) {
log.info("recover");
// Some code here that can throw exceptions.
throw new RuntimeException();
}
}方面类:
@Component
@Slf4j
public class TEST_MyAspect {
@Pointcut("execution(* packgName.otherProj.services.TEST_MyProcessService.triggerJob(..))")
public void MyTriggerJobs() {
}
@Pointcut("execution(* packgName.otherProj.services.TEST_MyProcessService.recover(..))")
public void MyRecoverJobs() {
}
@Before("MyTriggerJobs()")
public void beforeMyTriggerJobsAdvice(JoinPoint joinPoint) {
log.info("log beforeMyTriggerJobsAdvice");
}
@AfterReturning("MyTriggerJobs()")
public void afterMyTriggerJobsAdvice(JoinPoint joinPoint) {
log.info("log afterMyTriggerJobsAdvice");
}
@AfterThrowing(value = "MyTriggerJobs()", throwing = "error")
public void onErrorMyTriggerJobsAdvice(JoinPoint joinPoint, Throwable error) {
log.info("log onErrorMyTriggerJobsAdvice");
}
@Before("MyRecoverJobs()")
public void beforeMyRecoverJobsAdvice(JoinPoint joinPoint) {
log.info("log beforeMyRecoverJobsAdvice");
}
@AfterThrowing(value = "MyRecoverJobs()", throwing = "error")
public void onErrorRecoveryMyTriggerJobsAdvice(JoinPoint joinPoint, Throwable error) {
log.info("log onErrorRecoveryMyTriggerJobsAdvice");
}
@AfterReturning("MyRecoverJobs()")
public void afterRecoveryMyTriggerJobsAdvice(JoinPoint joinPoint) {
log.info("log afterRecoveryMyTriggerJobsAdvice");
}
}日志输出:
2021-06-02 20:56:00.016 INFO [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.c.TEST_ScheduledProcessPoller : scheduleTaskWithFixedDelay
2021-06-02 20:56:00.016 INFO [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.c.TEST_ScheduledProcessPoller : triggerBdaJobs
2021-06-02 20:56:00.051 INFO [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.component.aspect.TEST_BdaAspect : log beforeBdaTriggerJobsAdvice
2021-06-02 20:56:00.060 INFO [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.services.TEST_BdaProcessService : triggerJob
2021-06-02 20:56:00.061 INFO [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.component.aspect.TEST_BdaAspect : log onErrorBdaTriggerJobsAdvice
2021-06-02 20:56:05.065 INFO [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.component.aspect.TEST_BdaAspect : log beforeBdaTriggerJobsAdvice
2021-06-02 20:56:05.066 INFO [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.services.TEST_BdaProcessService : triggerJob
2021-06-02 20:56:05.066 INFO [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.component.aspect.TEST_BdaAspect : log onErrorBdaTriggerJobsAdvice
2021-06-02 20:56:10.070 INFO [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.component.aspect.TEST_BdaAspect : log beforeBdaTriggerJobsAdvice
2021-06-02 20:56:10.070 INFO [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.services.TEST_BdaProcessService : triggerJob
2021-06-02 20:56:10.070 INFO [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.component.aspect.TEST_BdaAspect : log onErrorBdaTriggerJobsAdvice
2021-06-02 20:56:10.070 INFO [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.services.TEST_BdaProcessService : recover
2021-06-02 20:56:10.070 ERROR [,60b7f0602b2ecb0deefc04d6840b6274,eefc04d6840b6274,true] 92605 --- [ scheduling-1] c.c.p.r.c.TEST_ScheduledProcessPoller : null发布于 2021-06-04 05:28:39
我不是Spring用户,但对AOP的所有内容都感兴趣,包括AspectJ和Spring。我喜欢你的小谜题。多亏了您的https://stackoverflow.com/help/mcve,我才得以重现问题并调试到其中。这是一个完美的例子,说明为什么MCVE比简单地发布一堆代码片段要好得多。因此,谢谢你,请继续用这种方式提问。
当查看调试器中的情况时,您会看到,当方面继续进入triggerJob时,在某个时候我们使用的是AnnotationAwareRetryOperationsInterceptor.invoke方法,在这里我们有以下代码:
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
MethodInterceptor delegate = getDelegate(invocation.getThis(), invocation.getMethod());
if (delegate != null) {
return delegate.invoke(invocation);
}
else {
return invocation.proceed();
}
}此时,Spring做出了构造委托的选择,该委托稍后将用于调用recover。它是用目标对象invocation.getThis()构造的,它指向原始对象,即TEST_BdaProcessService实例。此时,代码可以直接使用invocation.getProxy(),这将指向AOP代理,即TEST_BdaProcessService$$EnhancerBySpringCGLIB$$2f8076ac实例。问题是,目标对象引用被传递到调用recover的点,而此时对应的恢复性实例只知道目标对象,不再知道相应的代理对象。
当我实验将代理分配给委托作为目标时,您的通知方法就会被调用。
所以我们在这里讨论的是一个弹簧限制。我不知道这是一个故意的选择,是为了避免任何其他相关的问题,还是仅仅是一个疏忽。

更新:我代表您创建了春季重试问题#244。你想订阅它,这样你就可以知道它是否会/什么时候被修复。
更新2:问题已经解决,合并到主分支中,并且可能会成为即将发布的1.3.2版本的一部分。现在,您只需复制Spring重试,自行构建并使用快照即可。我重新测试了你的MCVE,这方面现在开始如预期的恢复方法。
https://stackoverflow.com/questions/67810277
复制相似问题