我正在尝试弹簧重试,我面临一个奇怪的问题。当我在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
在控制器中使用时无效?
答案 0 :(得分:9)
您看到的问题是由于您调用getInfo()
方法的方式。
在第一个示例中,您从同一个Spring托管bean中调用getInfo()
。在第二个示例中,您从另一个Spring托管bean调用getInfo()
。这种区别是微妙的,但非常重要,很可能是导致问题的原因。
当您使用@Retryable
注释时,Spring会在原始bean周围创建一个代理,以便他们可以在特殊情况下进行特殊处理。在这种特定情况下,Spring会应用一个建议,该建议会将调用委托给您的实际方法,捕获它可能抛出的RuntimeException
,并根据{{@Retryable
的配置重试调用方法。 1}}注释。
此代理在您的情况下重要的原因是只有外部呼叫者才能看到代理建议。你的bean不知道它是代理的,只知道它的方法被调用(通过代理建议)。当bean调用自身的方法时,不会涉及进一步的代理,这就是重试实际上不会发生的原因。