code: @Override
  	public Object invoke(final MethodInvocation invocation) throws Throwable {
  
  		if (this.logger.isDebugEnabled()) {
  			this.logger.debug("Executing proxied method in stateful retry: "
  					+ invocation.getStaticPart() + "("
  					+ ObjectUtils.getIdentityHexString(invocation) + ")");
  		}
  
  		Object[] args = invocation.getArguments();
  		Object defaultKey = Arrays.asList(args);
  		if (args.length == 1) {
  			defaultKey = args[0];
  		}
  
  		Object key = createKey(invocation, defaultKey);
  		RetryState retryState = new DefaultRetryState(key,
  				this.newMethodArgumentsIdentifier != null
  						&& this.newMethodArgumentsIdentifier.isNew(args),
  				this.rollbackClassifier);
  
  		Object result = this.retryOperations
  				.execute(new StatefulMethodInvocationRetryCallback(invocation, label),
  						this.recoverer != null
  								? new ItemRecovererCallback(args, this.recoverer) : null,
  						retryState);
  
  		if (this.logger.isDebugEnabled()) {
  			this.logger.debug("Exiting proxied method in stateful retry with result: ("
  					+ result + ")");
  		}
  
  		return result;
  
  	}
docs: Wrap the method invocation in a stateful retry with the policy and other helpers
  provided. If there is a failure the exception will generally be re-thrown. The only
  time it is not re-thrown is when retry is exhausted and the recovery path is taken
  (though the {@link MethodInvocationRecoverer} provided if there is one). In that
  case the value returned from the method invocation will be the value returned by
  the recoverer (so the return type for that should be the same as the intercepted
  method).
  @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
  @see MethodInvocationRecoverer#recover(Object[], Throwable)