9

我正在尝试春季重试,但遇到了一个奇怪的问题。当我在 Rest Controller 中的方法上使用重试注释时,重试不起作用。但是,如果我将该方法移至单独的服务类,它就可以工作。以下代码不起作用:

@RestController
public class HelloController {

    @RequestMapping(value = "/hello")
    public String hello() {
        return getInfo();
    }

    @Retryable(RuntimeException.class)
    public String getInfo() {
        Random random = new Random();
        int r = random.nextInt(2);
        if (r == 1) {
            throw new RuntimeException();
        } else {
            return "Success";
        }
    }
}

但以下是:

@RestController
public class HelloController {

    @Autowired
    private SomeService service;

    @RequestMapping(value = "/hello")
    public String hello() {
        String result = service.getInfo();
        return result;
    }
}

@Service
public class SomeService {

    @Retryable(RuntimeException.class)
    public String getInfo() {
        Random random = new Random();
        int r = random.nextInt(2);
        if (r == 1) {
            throw new RuntimeException();
        } else {
            return "Success";
        }
    }
}

我的问题是为什么@Retryable在控制器中使用时不起作用?

4

1 回答 1

21

您看到的问题是由于您调用getInfo()方法的方式。

在第一个示例中,您getInfo()从同一个 spring 托管 bean 中调用。在第二个示例中,您getInfo()从不同的 spring 托管 bean 调用。这种区别很微妙,但非常重要,很可能是导致您的问题的原因。

当您使用@Retryable注解时,Spring 会围绕您的原始 bean 创建一个代理,以便它们可以在特殊情况下进行特殊处理。在这种特定情况下,Spring 应用 Advice 将调用委托给您的实际方法,捕获RuntimeException它可能抛出的 's,并根据@Retryable注释的配置重试方法的调用。

此代理在您的情况下很重要的原因是只有外部调用者才能看到代理建议。您的 bean 不知道它被代理了,只知道它的方法正在被调用(通过代理建议)。当您的 bean 调用自身的方法时,不涉及进一步的代理,这就是实际上不会发生重试的原因。

于 2016-04-05T05:25:42.593 回答