当前位置: 首页 > 知识库问答 >
问题:

@recover方法未被Spring AOP通知截获

颛孙航
2023-03-14

在使用Spring/Java和面向方面编程编写代码时,我面临着一个问题。在服务类中,我有使用@retryable的重试方法和使用@recovery的恢复方法。

这两个方法中的每一个都附加到方面。TestProcessService中的可重试方法“TriggerJob”附加到TestAspect类中的这些方法--BeforeTestTriggerJobsAdvision、AfterTestTriggerJobsAdvision、OnErrorTestTriggerJobsAdvision。它们都工作得很好,并在正确的时间被触发。

问题陈述:TestProcessService中的恢复方法“Recovery”附加到TestAspect类中的这些方法--beforeRecoveryTestJobsAdvision、OnerrorRecoveryTestTriggerJobsAdvision和AfterRecoveryTestTriggerJobsAdvision。

但是,一旦代码到达TestProcessService中的恢复方法,就不会调用这些方面方法。

代码如下:

@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

共有1个答案

施宏大
2023-03-14

我不是Spring用户,但对所有AOP都感兴趣,包括AspectJ和Spring AOP。我喜欢你的小拼图。多亏了你的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选择构造委托,稍后将用于调用recovere。它是用目标对象invocation.getthis()构造的,它指向原始对象,即test_bdaProcessService实例。此时,代码可以简单地使用invocation.getproxy(),这将指向AOP代理,即test_bdaProcessService$$EnhancerbySpringCglib$$2f8076ac实例。问题是,目标对象引用被传递到调用recovery的地方,此时对应的恢复程序实例只知道目标对象,不再知道对应的代理对象。

当我将代理作为目标分配给委托时,调用了您的advision方法。

所以我们在这里讨论的是一个Spring限制。我不知道这是为了避免任何其他相关问题而深思熟虑的选择,还是只是一个疏忽。

更新:我代表您创建了Spring重试问题#244。你想订阅它,这样你就可以知道它是否/何时将被修复。

更新2:这个问题已经被修复,被合并到主分支中,并且可能会成为即将发布的1.3.2版本的一部分。现在,您只需克隆Spring重试,自己构建并使用快照。我对您的MCVE进行了重新测试,现在恢复方法的方面就像预期的那样开始了。

 类似资料:
  • 问题很简单 在这里打破头! 编辑:一个小突破。我打印了目标,它返回的是SimpleJPrepository,而不是实际的存储库。

  • 我正在使用Spring AOP实现自定义注释处理。我有下面的代码。 //应用程序配置 出于测试目的,我创建了下面的bean 无论何时启动应用程序,它都将调用TestCacheDemo类的test方法,并将定时器设置为在3秒后触发,这样我就可以从timer任务的run方法内部调用带注释的方法getCacheValue。但是当调用带注释的方法时,不会调用注释处理器。因此,我无法进行注释处理。请让我知道

  • 对于上面的modbus轮询查询,我没有得到哪个是crc值,以及使用了什么类型的crc。它是怎么来的,77是设备的id。请指引我。 我从轮询设备得到以下响应

  • 我已在centos 7上更新了我的应用程序服务器。使用PHP7.3实现x。当我运行控制台命令时,会出现如下错误 下面是堆栈日志。 我不明白该往哪里看,可能是什么问题。请引导任何人。

  • 我正在测试一个Spring重试,但似乎没有调用恢复。试图让它工作,但似乎详尽无遗。我传递给@recover no argument,Throwable,exception。改变了重试依赖的版本,似乎它包含在spring boot的aop中,并删除了它。Creading Geting Recovery没有被调用,出现以下异常Messege。 请求处理失败;嵌套异常是org.springframewo

  • 我想训练一个模特。我有将近150个类,我正在使用ImageDataGenerator来扩展我的数据集。我还使用模型检查点和csvlogger来保存权重。当我开始训练时,它在第一个纪元的某个时刻给了我一个错误。如果有帮助的话,我使用的图像是灰度图像。 这是我的代码: 这是我的回拨: 拟合模型: 我收到的错误是这样的: 纪元 1/10 3428/4128 [======================