To get some control over the retry duration, I added the Spring @Retryable attribute to the same method that the @JMSListener is on. In the queue itself, I set the "backout threshold" to 1 and defined a "backout queue" to send the message to when the threshold was exceeded.
For catching code exceptions where the retry logic should fire, make sure the @Recover method throws either a JmsException, RuntimeException, or a similar exception that inherits from those two.
For catching code exceptions where the retry logic should not fire, make sure the @Recover method logs and swallows the exception. Do not rethrow.
With this solution, the @Retryable attribute is controlling the retry logic.
The queue's backout threshold is responsible for moving the message after the retry logic has failed... and nothing more.
Note that I'm an IBM MQ and JMS newbie, so if this solution is not a good one, please advise as to why it isn't.