为什么无法同时使用@PostConstruct和@Retryable?

3
我使用基于Spring框架的AnnotationConfigApplicationContext创建了一个应用程序。其中一个bean具有一个init方法,用于创建到外部服务的连接。这个方法可以用@PostConstruct注解,以便在bean初始化后自动运行。当创建此连接时遇到异常时,我希望我的init方法重试最多5次。但是,当我同时使用@PostConstruct@Retryable注解该方法时,发现异常只被抛出一次,然后程序退出——似乎@Retryable没有起作用。
我已经在配置类中正确使用了@EnableRetry,并与@Configuration一起使用。我在同一个bean上创建了另一个被注释为retryable的方法B,如果在bean实例化后调用此方法,则可以看到当抛出异常时该方法被重试/表现如预期。
我认为原因可能是与切面相关或者@Postconstruct发生在spring-retry元素附加之前?
实际上是否有更好的方法来处理初始化方法中的异常,并通过注释使其可重试,而不是在方法中编程尝试?
编辑:我现在同意不应该通过@Postconstruct创建与外部服务的连接。如果重试失败,这可能会阻止整个上下文初始化,这可能会产生不利影响。
然而,这还没有回答Spring框架的哪个部分不允许这两个注解同时起作用的问题。
3个回答

3
为了在创建此连接时处理任何异常,我希望我的init方法在捕获异常后重试最多5次。
永远不要在init方法中连接资源;你应该先等待上下文的创建。
最好实现SmartLifecycle并在start()中连接。这样,你可以确保整个上下文在连接到外部资源之前已经初始化完成。
这种情况下,retry拦截器应该建议start()方法。
正如@Naros所建议的,ContextRefreshedEvent是另一种选择,但是你不应该在@PostConstruct中做这样的事情。

谢谢建议,Gary。但接下来该怎么做呢?在start()中连接 - 然后如何将此连接传递给特定的bean?在阅读您的答案之前,我一直想在PostConstruct中尝试5次获取连接(在我的情况下:当前的hazelcast实例)。 - Woland
请勿在评论中提出新问题。只需将代码从“post construct”方法移动到“SmartLifecyle.start()”即可。如果这不足够,请提出新问题。 - Gary Russell

2

我建议您实际应用一个ApplicationListener,监听ContextRefreshedEvent事件,并使用您的重试逻辑注释此方法。这些侦听器在上下文完全刷新并且所有bean已正确配置和连接后才会触发。


1
将PostConstruct替换为EventListener({ContextRefreshedEvent.class}),再结合Retryable,就可以获得所需的行为。我相信在应用程序的生命周期中,上下文可能会被刷新多次,因此它可能不是我正在寻找的解决方案。创建连接的方法应该只运行一次,并重试X次。 - UserF40
是的,一个上下文可以被多次刷新,但在每次刷新时,Spring 上下文中的 Bean 都会被重新创建,因此它应该无论如何都会给您想要的行为。 - Naros

2
'

'@Retryable'是由'spring-retry'应用的一个方面。这只有在Spring上下文完全创建后才会附加,也就是在所有带有'@PostConstruct'注释的方法运行之后,因此'@Retryable'没有任何效果。

'

我认为更好的答案是下面这个:https://dev59.com/7Jjga4cB1Zd3GeqPMpgf#38175918 - Beto Neto
@BetoNeto,任何问题的最简单答案就是停止遇到这个问题。我认为最好的答案是由OP提供的,因为他解释了内部部分。现在读者可以根据自己所掌握的知识使用他认为更好的方法。 - skryvets

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接