Allow to customize the RabbitMQ RetryTemplate
This commit adds the ability to customize the RetryTemplate used in the RabbitMQ infrastructure. The customizer is slightly unusual and offer a `Target` enum that define the component that will use the retry template: `SENDER` for the auto-configured `RabbitTemplate` and `LISTENER` for a listener container created by a `RabbitListenerContainerFactoryConfigurer`. Closes gh-13793
This commit is contained in:
		
							parent
							
								
									58efd1b51a
								
							
						
					
					
						commit
						ada699a9f6
					
				|  | @ -16,6 +16,8 @@ | ||||||
| 
 | 
 | ||||||
| package org.springframework.boot.autoconfigure.amqp; | package org.springframework.boot.autoconfigure.amqp; | ||||||
| 
 | 
 | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
| import org.springframework.amqp.rabbit.config.AbstractRabbitListenerContainerFactory; | import org.springframework.amqp.rabbit.config.AbstractRabbitListenerContainerFactory; | ||||||
| import org.springframework.amqp.rabbit.config.RetryInterceptorBuilder; | import org.springframework.amqp.rabbit.config.RetryInterceptorBuilder; | ||||||
| import org.springframework.amqp.rabbit.connection.ConnectionFactory; | import org.springframework.amqp.rabbit.connection.ConnectionFactory; | ||||||
|  | @ -40,6 +42,8 @@ public abstract class AbstractRabbitListenerContainerFactoryConfigurer<T extends | ||||||
| 
 | 
 | ||||||
| 	private MessageRecoverer messageRecoverer; | 	private MessageRecoverer messageRecoverer; | ||||||
| 
 | 
 | ||||||
|  | 	private List<RabbitRetryTemplateCustomizer> retryTemplateCustomizers; | ||||||
|  | 
 | ||||||
| 	private RabbitProperties rabbitProperties; | 	private RabbitProperties rabbitProperties; | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
|  | @ -59,6 +63,15 @@ public abstract class AbstractRabbitListenerContainerFactoryConfigurer<T extends | ||||||
| 		this.messageRecoverer = messageRecoverer; | 		this.messageRecoverer = messageRecoverer; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Set the {@link RabbitRetryTemplateCustomizer} instances to use. | ||||||
|  | 	 * @param retryTemplateCustomizers the retry template customizers | ||||||
|  | 	 */ | ||||||
|  | 	protected void setRetryTemplateCustomizers( | ||||||
|  | 			List<RabbitRetryTemplateCustomizer> retryTemplateCustomizers) { | ||||||
|  | 		this.retryTemplateCustomizers = retryTemplateCustomizers; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Set the {@link RabbitProperties} to use. | 	 * Set the {@link RabbitProperties} to use. | ||||||
| 	 * @param rabbitProperties the {@link RabbitProperties} | 	 * @param rabbitProperties the {@link RabbitProperties} | ||||||
|  | @ -108,7 +121,9 @@ public abstract class AbstractRabbitListenerContainerFactoryConfigurer<T extends | ||||||
| 					? RetryInterceptorBuilder.stateless() | 					? RetryInterceptorBuilder.stateless() | ||||||
| 					: RetryInterceptorBuilder.stateful()); | 					: RetryInterceptorBuilder.stateful()); | ||||||
| 			builder.retryOperations( | 			builder.retryOperations( | ||||||
| 					new RetryTemplateFactory().createRetryTemplate(retryConfig)); | 					new RetryTemplateFactory(this.retryTemplateCustomizers) | ||||||
|  | 							.createRetryTemplate(retryConfig, | ||||||
|  | 									RabbitRetryTemplateCustomizer.Target.LISTENER)); | ||||||
| 			MessageRecoverer recoverer = (this.messageRecoverer != null | 			MessageRecoverer recoverer = (this.messageRecoverer != null | ||||||
| 					? this.messageRecoverer : new RejectAndDontRequeueRecoverer()); | 					? this.messageRecoverer : new RejectAndDontRequeueRecoverer()); | ||||||
| 			builder.recoverer(recoverer); | 			builder.recoverer(recoverer); | ||||||
|  |  | ||||||
|  | @ -16,6 +16,8 @@ | ||||||
| 
 | 
 | ||||||
| package org.springframework.boot.autoconfigure.amqp; | package org.springframework.boot.autoconfigure.amqp; | ||||||
| 
 | 
 | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
| import org.springframework.amqp.rabbit.annotation.EnableRabbit; | import org.springframework.amqp.rabbit.annotation.EnableRabbit; | ||||||
| import org.springframework.amqp.rabbit.config.DirectRabbitListenerContainerFactory; | import org.springframework.amqp.rabbit.config.DirectRabbitListenerContainerFactory; | ||||||
| import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils; | import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils; | ||||||
|  | @ -45,13 +47,17 @@ class RabbitAnnotationDrivenConfiguration { | ||||||
| 
 | 
 | ||||||
| 	private final ObjectProvider<MessageRecoverer> messageRecoverer; | 	private final ObjectProvider<MessageRecoverer> messageRecoverer; | ||||||
| 
 | 
 | ||||||
|  | 	private final ObjectProvider<List<RabbitRetryTemplateCustomizer>> retryTemplateCustomizers; | ||||||
|  | 
 | ||||||
| 	private final RabbitProperties properties; | 	private final RabbitProperties properties; | ||||||
| 
 | 
 | ||||||
| 	RabbitAnnotationDrivenConfiguration(ObjectProvider<MessageConverter> messageConverter, | 	RabbitAnnotationDrivenConfiguration(ObjectProvider<MessageConverter> messageConverter, | ||||||
| 			ObjectProvider<MessageRecoverer> messageRecoverer, | 			ObjectProvider<MessageRecoverer> messageRecoverer, | ||||||
|  | 			ObjectProvider<List<RabbitRetryTemplateCustomizer>> retryTemplateCustomizers, | ||||||
| 			RabbitProperties properties) { | 			RabbitProperties properties) { | ||||||
| 		this.messageConverter = messageConverter; | 		this.messageConverter = messageConverter; | ||||||
| 		this.messageRecoverer = messageRecoverer; | 		this.messageRecoverer = messageRecoverer; | ||||||
|  | 		this.retryTemplateCustomizers = retryTemplateCustomizers; | ||||||
| 		this.properties = properties; | 		this.properties = properties; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -61,6 +67,8 @@ class RabbitAnnotationDrivenConfiguration { | ||||||
| 		SimpleRabbitListenerContainerFactoryConfigurer configurer = new SimpleRabbitListenerContainerFactoryConfigurer(); | 		SimpleRabbitListenerContainerFactoryConfigurer configurer = new SimpleRabbitListenerContainerFactoryConfigurer(); | ||||||
| 		configurer.setMessageConverter(this.messageConverter.getIfUnique()); | 		configurer.setMessageConverter(this.messageConverter.getIfUnique()); | ||||||
| 		configurer.setMessageRecoverer(this.messageRecoverer.getIfUnique()); | 		configurer.setMessageRecoverer(this.messageRecoverer.getIfUnique()); | ||||||
|  | 		configurer.setRetryTemplateCustomizers( | ||||||
|  | 				this.retryTemplateCustomizers.getIfAvailable()); | ||||||
| 		configurer.setRabbitProperties(this.properties); | 		configurer.setRabbitProperties(this.properties); | ||||||
| 		return configurer; | 		return configurer; | ||||||
| 	} | 	} | ||||||
|  | @ -82,6 +90,8 @@ class RabbitAnnotationDrivenConfiguration { | ||||||
| 		DirectRabbitListenerContainerFactoryConfigurer configurer = new DirectRabbitListenerContainerFactoryConfigurer(); | 		DirectRabbitListenerContainerFactoryConfigurer configurer = new DirectRabbitListenerContainerFactoryConfigurer(); | ||||||
| 		configurer.setMessageConverter(this.messageConverter.getIfUnique()); | 		configurer.setMessageConverter(this.messageConverter.getIfUnique()); | ||||||
| 		configurer.setMessageRecoverer(this.messageRecoverer.getIfUnique()); | 		configurer.setMessageRecoverer(this.messageRecoverer.getIfUnique()); | ||||||
|  | 		configurer.setRetryTemplateCustomizers( | ||||||
|  | 				this.retryTemplateCustomizers.getIfAvailable()); | ||||||
| 		configurer.setRabbitProperties(this.properties); | 		configurer.setRabbitProperties(this.properties); | ||||||
| 		return configurer; | 		return configurer; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
| package org.springframework.boot.autoconfigure.amqp; | package org.springframework.boot.autoconfigure.amqp; | ||||||
| 
 | 
 | ||||||
| import java.time.Duration; | import java.time.Duration; | ||||||
|  | import java.util.List; | ||||||
| 
 | 
 | ||||||
| import com.rabbitmq.client.Channel; | import com.rabbitmq.client.Channel; | ||||||
| 
 | 
 | ||||||
|  | @ -151,15 +152,18 @@ public class RabbitAutoConfiguration { | ||||||
| 	@Import(RabbitConnectionFactoryCreator.class) | 	@Import(RabbitConnectionFactoryCreator.class) | ||||||
| 	protected static class RabbitTemplateConfiguration { | 	protected static class RabbitTemplateConfiguration { | ||||||
| 
 | 
 | ||||||
| 		private final ObjectProvider<MessageConverter> messageConverter; |  | ||||||
| 
 |  | ||||||
| 		private final RabbitProperties properties; | 		private final RabbitProperties properties; | ||||||
| 
 | 
 | ||||||
| 		public RabbitTemplateConfiguration( | 		private final ObjectProvider<MessageConverter> messageConverter; | ||||||
|  | 
 | ||||||
|  | 		private final ObjectProvider<List<RabbitRetryTemplateCustomizer>> retryTemplateCustomizers; | ||||||
|  | 
 | ||||||
|  | 		public RabbitTemplateConfiguration(RabbitProperties properties, | ||||||
| 				ObjectProvider<MessageConverter> messageConverter, | 				ObjectProvider<MessageConverter> messageConverter, | ||||||
| 				RabbitProperties properties) { | 				ObjectProvider<List<RabbitRetryTemplateCustomizer>> retryTemplateCustomizers) { | ||||||
| 			this.messageConverter = messageConverter; |  | ||||||
| 			this.properties = properties; | 			this.properties = properties; | ||||||
|  | 			this.messageConverter = messageConverter; | ||||||
|  | 			this.retryTemplateCustomizers = retryTemplateCustomizers; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		@Bean | 		@Bean | ||||||
|  | @ -175,8 +179,10 @@ public class RabbitAutoConfiguration { | ||||||
| 			template.setMandatory(determineMandatoryFlag()); | 			template.setMandatory(determineMandatoryFlag()); | ||||||
| 			RabbitProperties.Template properties = this.properties.getTemplate(); | 			RabbitProperties.Template properties = this.properties.getTemplate(); | ||||||
| 			if (properties.getRetry().isEnabled()) { | 			if (properties.getRetry().isEnabled()) { | ||||||
| 				template.setRetryTemplate(new RetryTemplateFactory() | 				template.setRetryTemplate(new RetryTemplateFactory( | ||||||
| 						.createRetryTemplate(properties.getRetry())); | 						this.retryTemplateCustomizers.getIfAvailable()) | ||||||
|  | 								.createRetryTemplate(properties.getRetry(), | ||||||
|  | 										RabbitRetryTemplateCustomizer.Target.SENDER)); | ||||||
| 			} | 			} | ||||||
| 			map.from(properties::getReceiveTimeout).whenNonNull().as(Duration::toMillis) | 			map.from(properties::getReceiveTimeout).whenNonNull().as(Duration::toMillis) | ||||||
| 					.to(template::setReceiveTimeout); | 					.to(template::setReceiveTimeout); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,58 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright 2012-2018 the original author or authors. | ||||||
|  |  * | ||||||
|  |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  |  * you may not use this file except in compliance with the License. | ||||||
|  |  * You may obtain a copy of the License at | ||||||
|  |  * | ||||||
|  |  *      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  |  * | ||||||
|  |  * Unless required by applicable law or agreed to in writing, software | ||||||
|  |  * distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  |  * See the License for the specific language governing permissions and | ||||||
|  |  * limitations under the License. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package org.springframework.boot.autoconfigure.amqp; | ||||||
|  | 
 | ||||||
|  | import org.springframework.amqp.rabbit.core.RabbitTemplate; | ||||||
|  | import org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer; | ||||||
|  | import org.springframework.retry.support.RetryTemplate; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Callback interface that can be used to customize a {@link RetryTemplate} used as part | ||||||
|  |  * of the Rabbit infrastructure. | ||||||
|  |  * | ||||||
|  |  * @author Stephane Nicoll | ||||||
|  |  * @since 2.1.0 | ||||||
|  |  */ | ||||||
|  | @FunctionalInterface | ||||||
|  | public interface RabbitRetryTemplateCustomizer { | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Callback to customize a {@link RetryTemplate} instance used in the context of the | ||||||
|  | 	 * specified {@link Target}. | ||||||
|  | 	 * @param target the {@link Target} of the retry template | ||||||
|  | 	 * @param retryTemplate the template to customize | ||||||
|  | 	 */ | ||||||
|  | 	void customize(Target target, RetryTemplate retryTemplate); | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Define the available target for a {@link RetryTemplate}. | ||||||
|  | 	 */ | ||||||
|  | 	enum Target { | ||||||
|  | 
 | ||||||
|  | 		/** | ||||||
|  | 		 * {@link RabbitTemplate} target. | ||||||
|  | 		 */ | ||||||
|  | 		SENDER, | ||||||
|  | 
 | ||||||
|  | 		/** | ||||||
|  | 		 * {@link AbstractMessageListenerContainer} target. | ||||||
|  | 		 */ | ||||||
|  | 		LISTENER | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
| package org.springframework.boot.autoconfigure.amqp; | package org.springframework.boot.autoconfigure.amqp; | ||||||
| 
 | 
 | ||||||
| import java.time.Duration; | import java.time.Duration; | ||||||
|  | import java.util.List; | ||||||
| 
 | 
 | ||||||
| import org.springframework.boot.context.properties.PropertyMapper; | import org.springframework.boot.context.properties.PropertyMapper; | ||||||
| import org.springframework.retry.backoff.ExponentialBackOffPolicy; | import org.springframework.retry.backoff.ExponentialBackOffPolicy; | ||||||
|  | @ -31,7 +32,14 @@ import org.springframework.retry.support.RetryTemplate; | ||||||
|  */ |  */ | ||||||
| class RetryTemplateFactory { | class RetryTemplateFactory { | ||||||
| 
 | 
 | ||||||
| 	public RetryTemplate createRetryTemplate(RabbitProperties.Retry properties) { | 	private final List<RabbitRetryTemplateCustomizer> customizers; | ||||||
|  | 
 | ||||||
|  | 	RetryTemplateFactory(List<RabbitRetryTemplateCustomizer> customizers) { | ||||||
|  | 		this.customizers = customizers; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public RetryTemplate createRetryTemplate(RabbitProperties.Retry properties, | ||||||
|  | 			RabbitRetryTemplateCustomizer.Target target) { | ||||||
| 		PropertyMapper map = PropertyMapper.get(); | 		PropertyMapper map = PropertyMapper.get(); | ||||||
| 		RetryTemplate template = new RetryTemplate(); | 		RetryTemplate template = new RetryTemplate(); | ||||||
| 		SimpleRetryPolicy policy = new SimpleRetryPolicy(); | 		SimpleRetryPolicy policy = new SimpleRetryPolicy(); | ||||||
|  | @ -44,6 +52,11 @@ class RetryTemplateFactory { | ||||||
| 		map.from(properties::getMaxInterval).whenNonNull().as(Duration::toMillis) | 		map.from(properties::getMaxInterval).whenNonNull().as(Duration::toMillis) | ||||||
| 				.to(backOffPolicy::setMaxInterval); | 				.to(backOffPolicy::setMaxInterval); | ||||||
| 		template.setBackOffPolicy(backOffPolicy); | 		template.setBackOffPolicy(backOffPolicy); | ||||||
|  | 		if (this.customizers != null) { | ||||||
|  | 			for (RabbitRetryTemplateCustomizer customizer : this.customizers) { | ||||||
|  | 				customizer.customize(target, template); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 		return template; | 		return template; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ import org.springframework.amqp.core.AcknowledgeMode; | ||||||
| import org.springframework.amqp.core.AmqpAdmin; | import org.springframework.amqp.core.AmqpAdmin; | ||||||
| import org.springframework.amqp.core.Message; | import org.springframework.amqp.core.Message; | ||||||
| import org.springframework.amqp.rabbit.annotation.EnableRabbit; | import org.springframework.amqp.rabbit.annotation.EnableRabbit; | ||||||
|  | import org.springframework.amqp.rabbit.config.AbstractRabbitListenerContainerFactory; | ||||||
| import org.springframework.amqp.rabbit.config.DirectRabbitListenerContainerFactory; | import org.springframework.amqp.rabbit.config.DirectRabbitListenerContainerFactory; | ||||||
| import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils; | import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils; | ||||||
| import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; | import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; | ||||||
|  | @ -54,8 +55,11 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner; | ||||||
| import org.springframework.context.annotation.Bean; | import org.springframework.context.annotation.Bean; | ||||||
| import org.springframework.context.annotation.Configuration; | import org.springframework.context.annotation.Configuration; | ||||||
| import org.springframework.context.annotation.Primary; | import org.springframework.context.annotation.Primary; | ||||||
|  | import org.springframework.retry.RetryPolicy; | ||||||
|  | import org.springframework.retry.backoff.BackOffPolicy; | ||||||
| import org.springframework.retry.backoff.ExponentialBackOffPolicy; | import org.springframework.retry.backoff.ExponentialBackOffPolicy; | ||||||
| import org.springframework.retry.interceptor.MethodInvocationRecoverer; | import org.springframework.retry.interceptor.MethodInvocationRecoverer; | ||||||
|  | import org.springframework.retry.policy.NeverRetryPolicy; | ||||||
| import org.springframework.retry.policy.SimpleRetryPolicy; | import org.springframework.retry.policy.SimpleRetryPolicy; | ||||||
| import org.springframework.retry.support.RetryTemplate; | import org.springframework.retry.support.RetryTemplate; | ||||||
| 
 | 
 | ||||||
|  | @ -280,6 +284,28 @@ public class RabbitAutoConfigurationTests { | ||||||
| 				}); | 				}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void testRabbitTemplateRetryWithCustomizer() { | ||||||
|  | 		this.contextRunner | ||||||
|  | 				.withUserConfiguration(RabbitRetryTemplateCustomizerConfiguration.class) | ||||||
|  | 				.withPropertyValues("spring.rabbitmq.template.retry.enabled:true", | ||||||
|  | 						"spring.rabbitmq.template.retry.initialInterval:2000") | ||||||
|  | 				.run((context) -> { | ||||||
|  | 					RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class); | ||||||
|  | 					DirectFieldAccessor dfa = new DirectFieldAccessor(rabbitTemplate); | ||||||
|  | 					RetryTemplate retryTemplate = (RetryTemplate) dfa | ||||||
|  | 							.getPropertyValue("retryTemplate"); | ||||||
|  | 					assertThat(retryTemplate).isNotNull(); | ||||||
|  | 					dfa = new DirectFieldAccessor(retryTemplate); | ||||||
|  | 					assertThat(dfa.getPropertyValue("backOffPolicy")) | ||||||
|  | 							.isSameAs(context.getBean( | ||||||
|  | 									RabbitRetryTemplateCustomizerConfiguration.class).backOffPolicy); | ||||||
|  | 					ExponentialBackOffPolicy backOffPolicy = (ExponentialBackOffPolicy) dfa | ||||||
|  | 							.getPropertyValue("backOffPolicy"); | ||||||
|  | 					assertThat(backOffPolicy.getInitialInterval()).isEqualTo(100); | ||||||
|  | 				}); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void testRabbitTemplateExchangeAndRoutingKey() { | 	public void testRabbitTemplateExchangeAndRoutingKey() { | ||||||
| 		this.contextRunner.withUserConfiguration(TestConfiguration.class) | 		this.contextRunner.withUserConfiguration(TestConfiguration.class) | ||||||
|  | @ -470,6 +496,53 @@ public class RabbitAutoConfigurationTests { | ||||||
| 				}); | 				}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void testSimpleRabbitListenerContainerFactoryRetryWithCustomizer() { | ||||||
|  | 		this.contextRunner | ||||||
|  | 				.withUserConfiguration(RabbitRetryTemplateCustomizerConfiguration.class) | ||||||
|  | 				.withPropertyValues("spring.rabbitmq.listener.simple.retry.enabled:true", | ||||||
|  | 						"spring.rabbitmq.listener.simple.retry.maxAttempts:4") | ||||||
|  | 				.run((context) -> { | ||||||
|  | 					SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory = context | ||||||
|  | 							.getBean("rabbitListenerContainerFactory", | ||||||
|  | 									SimpleRabbitListenerContainerFactory.class); | ||||||
|  | 					assertListenerRetryTemplate(rabbitListenerContainerFactory, | ||||||
|  | 							context.getBean( | ||||||
|  | 									RabbitRetryTemplateCustomizerConfiguration.class).retryPolicy); | ||||||
|  | 				}); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void testDirectRabbitListenerContainerFactoryRetryWithCustomizer() { | ||||||
|  | 		this.contextRunner | ||||||
|  | 				.withUserConfiguration(RabbitRetryTemplateCustomizerConfiguration.class) | ||||||
|  | 				.withPropertyValues("spring.rabbitmq.listener.type:direct", | ||||||
|  | 						"spring.rabbitmq.listener.direct.retry.enabled:true", | ||||||
|  | 						"spring.rabbitmq.listener.direct.retry.maxAttempts:4") | ||||||
|  | 				.run((context) -> { | ||||||
|  | 					DirectRabbitListenerContainerFactory rabbitListenerContainerFactory = context | ||||||
|  | 							.getBean("rabbitListenerContainerFactory", | ||||||
|  | 									DirectRabbitListenerContainerFactory.class); | ||||||
|  | 					assertListenerRetryTemplate(rabbitListenerContainerFactory, | ||||||
|  | 							context.getBean( | ||||||
|  | 									RabbitRetryTemplateCustomizerConfiguration.class).retryPolicy); | ||||||
|  | 				}); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	private void assertListenerRetryTemplate( | ||||||
|  | 			AbstractRabbitListenerContainerFactory<?> rabbitListenerContainerFactory, | ||||||
|  | 			RetryPolicy retryPolicy) { | ||||||
|  | 		DirectFieldAccessor dfa = new DirectFieldAccessor(rabbitListenerContainerFactory); | ||||||
|  | 		Advice[] adviceChain = (Advice[]) dfa.getPropertyValue("adviceChain"); | ||||||
|  | 		assertThat(adviceChain).isNotNull(); | ||||||
|  | 		assertThat(adviceChain.length).isEqualTo(1); | ||||||
|  | 		dfa = new DirectFieldAccessor(adviceChain[0]); | ||||||
|  | 		RetryTemplate retryTemplate = (RetryTemplate) dfa | ||||||
|  | 				.getPropertyValue("retryOperations"); | ||||||
|  | 		dfa = new DirectFieldAccessor(retryTemplate); | ||||||
|  | 		assertThat(dfa.getPropertyValue("retryPolicy")).isSameAs(retryPolicy); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	public void testRabbitListenerContainerFactoryConfigurersAreAvailable() { | 	public void testRabbitListenerContainerFactoryConfigurersAreAvailable() { | ||||||
| 		this.contextRunner.withUserConfiguration(TestConfiguration.class) | 		this.contextRunner.withUserConfiguration(TestConfiguration.class) | ||||||
|  | @ -793,6 +866,33 @@ public class RabbitAutoConfigurationTests { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Configuration | ||||||
|  | 	protected static class RabbitRetryTemplateCustomizerConfiguration { | ||||||
|  | 
 | ||||||
|  | 		private final BackOffPolicy backOffPolicy = new ExponentialBackOffPolicy(); | ||||||
|  | 
 | ||||||
|  | 		private final RetryPolicy retryPolicy = new NeverRetryPolicy(); | ||||||
|  | 
 | ||||||
|  | 		@Bean | ||||||
|  | 		public RabbitRetryTemplateCustomizer rabbitTemplateRetryTemplateCustomizer() { | ||||||
|  | 			return (target, template) -> { | ||||||
|  | 				if (target.equals(RabbitRetryTemplateCustomizer.Target.SENDER)) { | ||||||
|  | 					template.setBackOffPolicy(this.backOffPolicy); | ||||||
|  | 				} | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Bean | ||||||
|  | 		public RabbitRetryTemplateCustomizer rabbitListenerRetryTemplateCustomizer() { | ||||||
|  | 			return (target, template) -> { | ||||||
|  | 				if (target.equals(RabbitRetryTemplateCustomizer.Target.LISTENER)) { | ||||||
|  | 					template.setRetryPolicy(this.retryPolicy); | ||||||
|  | 				} | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	@Configuration | 	@Configuration | ||||||
| 	@EnableRabbit | 	@EnableRabbit | ||||||
| 	protected static class EnableRabbitConfiguration { | 	protected static class EnableRabbitConfiguration { | ||||||
|  |  | ||||||
|  | @ -5362,7 +5362,16 @@ If necessary, any `org.springframework.amqp.core.Queue` that is defined as a bea | ||||||
| automatically used to declare a corresponding queue on the RabbitMQ instance. | automatically used to declare a corresponding queue on the RabbitMQ instance. | ||||||
| 
 | 
 | ||||||
| To retry operations, you can enable retries on the `AmqpTemplate` (for example, in the | To retry operations, you can enable retries on the `AmqpTemplate` (for example, in the | ||||||
| event that the broker connection is lost). Retries are disabled by default. | event that the broker connection is lost): | ||||||
|  | 
 | ||||||
|  | [source,properties,indent=0] | ||||||
|  | ---- | ||||||
|  | 	spring.rabbitmq.template.retry.enabled=true | ||||||
|  | 	spring.rabbitmq.template.retry.initial-interval=2s | ||||||
|  | ---- | ||||||
|  | 
 | ||||||
|  | Retries are disabled by default. You can also customize the `RetryTemplate` | ||||||
|  | programmatically by declaring a `RabbitRetryTemplateCustomizer` bean. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -5444,7 +5453,8 @@ You can enable retries to handle situations where your listener throws an except | ||||||
| default, `RejectAndDontRequeueRecoverer` is used, but you can define a `MessageRecoverer` | default, `RejectAndDontRequeueRecoverer` is used, but you can define a `MessageRecoverer` | ||||||
| of your own. When retries are exhausted, the message is rejected and either dropped or | of your own. When retries are exhausted, the message is rejected and either dropped or | ||||||
| routed to a dead-letter exchange if the broker is configured to do so. By default, | routed to a dead-letter exchange if the broker is configured to do so. By default, | ||||||
| retries are disabled. | retries are disabled. You can also customize the `RetryTemplate` programmatically by | ||||||
|  | declaring a `RabbitRetryTemplateCustomizer` bean. | ||||||
| 
 | 
 | ||||||
| IMPORTANT: By default, if retries are disabled and the listener throws an exception, the | IMPORTANT: By default, if retries are disabled and the listener throws an exception, the | ||||||
| delivery is retried indefinitely. You can modify this behavior in two ways: Set the | delivery is retried indefinitely. You can modify this behavior in two ways: Set the | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue