Spring Retry:如何在RetryListener的onError中访问并修改失败方法参数?
解决Spring Retry监听器无法访问方法参数的问题
你的问题核心在于Spring Retry的默认RetryListener没法直接拿到方法调用时的参数,而且监听器本身主要是做事件监听,没法直接修改重试时的参数。这里给你两种实用的解决方案,其中第一种更推荐,适配你的场景:
方案一:使用@Recover恢复方法(推荐)
这种方式不需要自定义监听器,直接利用Spring Retry提供的@Recover注解处理异常恢复逻辑,能直接获取原方法的所有参数,代码简洁且符合框架设计意图。
修改后的代码示例:
首先假设你有一个负责重新登录的LoginService(需要你自己实现具体的登录逻辑):
@Service public class LoginService { public String login(String username) { // 这里实现调用外部系统登录、返回新sessionToken的逻辑 return "new-valid-session-token"; } }
然后调整你的EmployeeService,添加@Recover方法:
@Service public class EmployeeService { @Autowired private LoginService loginService; // 指定仅对UnknownSessionException重试,最多重试1次(总共调用2次) @Retryable(retryFor = UnknownSessionException.class, maxAttempts = 2) public void createEmployee(Session session, String employeeName) throws UnknownSessionException { // 原方法逻辑:调用外部系统,可能抛出UnknownSessionException externalSystemClient.createEmployee(session.getSessionToken(), employeeName); } // 当createEmployee抛出UnknownSessionException时,自动触发此方法 @Recover public void recoverSessionError(UnknownSessionException ex, Session session, String employeeName) { // 1. 用原Session的用户名重新登录,获取新的sessionToken String newSessionToken = loginService.login(session.getUsername()); // 2. 创建新Session对象(因为你的Session是@Value不可变类,无法修改原对象的token) Session newValidSession = new Session(session.getUsername(), newSessionToken); // 3. 带着新Session重新调用原方法 createEmployee(newValidSession, employeeName); } }
方案二:通过RetryContext存储参数(适合必须用监听器的场景)
如果你一定要用自定义监听器,需要在原方法里把参数存入RetryContext,这样监听器就能在onError中拿到参数。不过要注意:监听器只能监听异常,没法直接修改原重试逻辑的参数,所以需要手动调用方法重试,还要自己控制重试次数。
修改后的代码示例:
1. 在EmployeeService中把参数存入RetryContext
@Service public class EmployeeService { @Retryable(listeners = "employeeRetryListener", retryFor = UnknownSessionException.class) public void createEmployee(Session session, String employeeName) throws UnknownSessionException { // 将参数存入RetryContext RetryContext context = RetrySynchronizationManager.getContext(); if (context != null) { context.setAttribute("currentSession", session); context.setAttribute("employeeName", employeeName); } // 原方法逻辑 externalSystemClient.createEmployee(session.getSessionToken(), employeeName); } }
2. 修改自定义监听器获取并处理参数
@Component public class EmployeeRetryListener extends RetryListenerSupport { @Autowired private LoginService loginService; @Autowired private EmployeeService employeeService; @Override public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) { if (throwable instanceof UnknownSessionException) { // 从RetryContext中取出参数 Session oldSession = (Session) context.getAttribute("currentSession"); String employeeName = (String) context.getAttribute("employeeName"); if (oldSession != null && employeeName != null) { // 重新登录获取新token String newToken = loginService.login(oldSession.getUsername()); Session newSession = new Session(oldSession.getUsername(), newToken); // 手动控制重试次数,避免和@Retryable配置冲突 if (context.getRetryCount() < context.getRetryPolicy().getMaxAttempts()) { try { employeeService.createEmployee(newSession, employeeName); } catch (UnknownSessionException e) { throw new RuntimeException("重试登录后仍无法创建员工", e); } } } } super.onError(context, callback, throwable); } }
额外提示:关于不可变的Session类
你的Session是@Value注解的不可变类(final字段、无setter),所以无法直接修改原对象的sessionToken,必须创建新的Session实例来传递有效token,两种方案都考虑到了这一点。
内容的提问来源于stack exchange,提问作者Vincent B.
相关产品推荐
相关产品推荐

