GH-1175: Add @SpringRabbitTest

Resolves https://github.com/spring-projects/spring-amqp/issues/1175

- provision boilerplate infrastructure

* Fix test name in doc

* Remove `@ContextConfiguration` from doc.
This commit is contained in:
Gary Russell
2020-03-13 11:15:24 -04:00
committed by GitHub
parent e8264283c8
commit f85a3849fc
12 changed files with 474 additions and 125 deletions

View File

@@ -396,7 +396,7 @@ project('spring-rabbit-junit') {
exclude group: 'org.springframework', module: 'spring-web'
}
api 'org.springframework:spring-web'
optionalApi 'org.junit.jupiter:junit-jupiter-api'
api 'org.junit.jupiter:junit-jupiter-api'
api "org.assertj:assertj-core:$assertjVersion"
optionalApi "ch.qos.logback:logback-classic:$logbackVersion"
optionalApi 'org.apache.logging.log4j:log4j-core'

View File

@@ -1,4 +1,5 @@
/**
* Provides support classes (Rules etc) for JUnit tests.
* Provides support classes (Rules etc. with no spring-rabbit dependencies) for JUnit
* tests.
*/
package org.springframework.amqp.rabbit.junit;

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2020 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
*
* https://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.amqp.rabbit.test.context;
import org.springframework.amqp.rabbit.annotation.RabbitBootstrapConfiguration;
import org.springframework.amqp.rabbit.config.AbstractRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.config.DirectRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.junit.BrokerRunningSupport;
import org.springframework.amqp.rabbit.junit.RabbitAvailableCondition;
import org.springframework.amqp.rabbit.test.context.SpringRabbitTest.ContainerType;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.util.Assert;
/**
* Adds infrastructure beans to the Spring test context.
*
* @author Gary Russell
* @since 2.3
*
*/
class SpringRabbitContextCustomizer implements ContextCustomizer {
private final SpringRabbitTest springRabbitTest;
SpringRabbitContextCustomizer(SpringRabbitTest test) {
this.springRabbitTest = test;
}
@Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
Assert.isInstanceOf(GenericApplicationContext.class, context);
GenericApplicationContext applicationContext = (GenericApplicationContext) context;
BrokerRunningSupport brokerRunning = RabbitAvailableCondition.getBrokerRunning();
CachingConnectionFactory cf;
if (brokerRunning != null) {
cf = new CachingConnectionFactory(brokerRunning.getConnectionFactory());
}
else {
cf = new CachingConnectionFactory(this.springRabbitTest.host(), this.springRabbitTest.port());
cf.setUsername(this.springRabbitTest.user());
cf.setPassword(this.springRabbitTest.password());
}
applicationContext.registerBean("autoConnectionFactory", CachingConnectionFactory.class, () -> cf);
applicationContext.registerBean("autoRabbitTemplate", RabbitTemplate.class, () -> new RabbitTemplate(cf));
applicationContext.registerBean("autoRabbitAdmin", RabbitAdmin.class, () -> new RabbitAdmin(cf));
AbstractRabbitListenerContainerFactory<?> factory;
if (this.springRabbitTest.containerType().equals(ContainerType.simple)) {
factory = new SimpleRabbitListenerContainerFactory();
}
else {
factory = new DirectRabbitListenerContainerFactory();
}
factory.setConnectionFactory(cf);
applicationContext.registerBean("autoContainerFactory", AbstractRabbitListenerContainerFactory.class,
() -> factory);
new RabbitBootstrapConfiguration().registerBeanDefinitions(null,
(BeanDefinitionRegistry) applicationContext.getBeanFactory());
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2020 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
*
* https://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.amqp.rabbit.test.context;
import java.util.List;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
/**
* The {@link ContextCustomizerFactory} implementation to produce a
* {@link SpringRabbitContextCustomizer} if a {@link SpringRabbitTest} annotation
* is present on the test class.
*
* @author Gary Russell
*
* @since 2.3
*
*/
class SpringRabbitContextCustomizerFactory implements ContextCustomizerFactory {
@Override
public ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
SpringRabbitTest test =
AnnotatedElementUtils.findMergedAnnotation(testClass, SpringRabbitTest.class);
return test != null ? new SpringRabbitContextCustomizer(test) : null;
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2020 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
*
* https://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.amqp.rabbit.test.context;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.amqp.rabbit.junit.RabbitAvailable;
/**
* Adds infrastructure beans to a Spring test context; do not use with Spring Boot since
* it has its own auto configuration mechanism.
*
* @author Gary Russell
* @since 2.3
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface SpringRabbitTest {
/**
* Container type.
*/
enum ContainerType {
simple, direct
}
/**
* Set the host when not using {@link RabbitAvailable}.
* @return the host.
*/
String host() default "localhost";
/**
* Set the port when not using {@link RabbitAvailable}.
* @return the port.
*/
int port() default 5672;
/**
* Set the user when not using {@link RabbitAvailable}.
* @return the user.
*/
String user() default "guest";
/**
* Set the password when not using {@link RabbitAvailable}.
* @return the password.
*/
String password() default "guest";
/**
* Set the container type to determine which container factory to configure.
* @return the type.
*/
ContainerType containerType() default ContainerType.simple;
}

View File

@@ -0,0 +1,4 @@
/**
* Provides classes relating to the test application context.
*/
package org.springframework.amqp.rabbit.test.context;

View File

@@ -0,0 +1,3 @@
# Spring Test ContextCustomizerFactories
org.springframework.test.context.ContextCustomizerFactory=\
org.springframework.amqp.rabbit.test.context.SpringRabbitContextCustomizerFactory

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2020 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
*
* https://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.amqp.rabbit.test.context;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.config.AbstractRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.junit.RabbitAvailable;
import org.springframework.amqp.rabbit.junit.RabbitAvailableCondition;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
* @author Gary Russell
* @since 2.3
*
*/
@RabbitAvailable
@SpringJUnitConfig
@SpringRabbitTest
public class SpringRabbitTestTests {
@Autowired
private RabbitTemplate template;
@SuppressWarnings("unused")
@Autowired
private RabbitAdmin admin;
@SuppressWarnings("unused")
@Autowired
private AbstractRabbitListenerContainerFactory<?> factory;
@SuppressWarnings("unused")
@Autowired
private RabbitListenerEndpointRegistry registry;
@Test
void testAutowiring() {
assertThat(((CachingConnectionFactory) template.getConnectionFactory()).getRabbitConnectionFactory())
.isSameAs(RabbitAvailableCondition.getBrokerRunning().getConnectionFactory());
}
@Configuration
public static class Config {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -22,6 +22,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
/**
* An {@link ImportBeanDefinitionRegistrar} class that registers
@@ -44,7 +45,9 @@ import org.springframework.core.type.AnnotationMetadata;
public class RabbitBootstrapConfiguration implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
public void registerBeanDefinitions(@Nullable AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(
RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)) {

View File

@@ -1,3 +1,4 @@
[[change-history]]
== Change History
This section describes what changes have been made as versions have changed.
@@ -9,6 +10,131 @@ See <<whats-new>>.
[[previous-whats-new]]
=== Previous Releases
==== Changes in 2.2 Since 2.1
This section describes the changes between version 2.1 and version 2.2.
===== Package Changes
The following classes/interfaces have been moved from `org.springframework.amqp.rabbit.core.support` to `org.springframework.amqp.rabbit.batch`:
* `BatchingStrategy`
* `MessageBatch`
* `SimpleBatchingStrategy`
In addition, `ListenerExecutionFailedException` has been moved from `org.springframework.amqp.rabbit.listener.exception` to `org.springframework.amqp.rabbit.support`.
===== Dependency Changes
JUnit (4) is now an optional dependency and will no longer appear as a transitive dependency.
The `spring-rabbit-junit` module is now a *compile* dependency in the `spring-rabbit-test` module for a better target application development experience when with only a single `spring-rabbit-test` we get the full stack of testing utilities for AMQP components.
===== "Breaking" API Changes
the JUnit (5) `RabbitAvailableCondition.getBrokerRunning()` now returns a `BrokerRunningSupport` instance instead of a `BrokerRunning`, which depends on JUnit 4.
It has the same API so it's just a matter of changing the class name of any references.
See <<junit5-conditions>> for more information.
===== ListenerContainer Changes
Messages with fatal exceptions are now rejected and NOT requeued, by default, even if the acknowledge mode is manual.
See <<exception-handling>> for more information.
Listener performance can now be monitored using Micrometer `Timer` s.
See <<micrometer>> for more information.
===== @RabbitListener Changes
You can now configure an `executor` on each listener, overriding the factory configuration, to more easily identify threads associated with the listener.
You can now override the container factory's `acknowledgeMode` property with the annotation's `ackMode` property.
See <<listener-property-overrides,overriding container factory properties>> for more information.
When using <<receiving-batch,batching>>, `@RabbitListener` methods can now receive a complete batch of messages in one call instead of getting them one-at-a-time.
When receiving batched messages one-at-a-time, the last message has the `isLastInBatch` message property set to true.
In addition, received batched messages now contain the `amqp_batchSize` header.
Listeners can also consume batches created in the `SimpleMessageListenerContainer`, even if the batch is not created by the producer.
See <<choose-container>> for more information.
Spring Data Projection interfaces are now supported by the `Jackson2JsonMessageConverter`.
See <<data-projection>> for more information.
The `Jackson2JsonMessageConverter` now assumes the content is JSON if there is no `contentType` property, or it is the default (`application/octet-string`).
See <<Jackson2JsonMessageConverter-from-message>> for more information.
Similarly. the `Jackson2XmlMessageConverter` now assumes the content is XML if there is no `contentType` property, or it is the default (`application/octet-string`).
See <<jackson2xml>> for more information.
When a `@RabbitListener` method returns a result, the bean and `Method` are now available in the reply message properties.
This allows configuration of a `beforeSendReplyMessagePostProcessor` to, for example, set a header in the reply to indicate which method was invoked on the server.
See <<async-annotation-driven-reply>> for more information.
You can now configure a `ReplyPostProcessor` to make modifications to a reply message before it is sent.
See <<async-annotation-driven-reply>> for more information.
===== AMQP Logging Appenders Changes
The Log4J and Logback `AmqpAppender` s now support a `verifyHostname` SSL option.
Also these appenders now can be configured to not add MDC entries as headers.
The `addMdcAsHeaders` boolean option has been introduces to configure such a behavior.
The appenders now support the `SaslConfig` property.
See <<logging>> for more information.
===== MessageListenerAdapter Changes
The `MessageListenerAdapter` provides now a new `buildListenerArguments(Object, Channel, Message)` method to build an array of arguments to be passed into target listener and an old one is deprecated.
See <<message-listener-adapter>> for more information.
===== Exchange/Queue Declaration Changes
The `ExchangeBuilder` and `QueueBuilder` fluent APIs used to create `Exchange` and `Queue` objects for declaration by `RabbitAdmin` now support "well known" arguments.
See <<builder-api>> for more information.
The `RabbitAdmin` has a new property `explicitDeclarationsOnly`.
See <<conditional-declaration>> for more information.
===== Connection Factory Changes
The `CachingConnectionFactory` has a new property `shuffleAddresses`.
When providing a list of broker node addresses, the list will be shuffled before creating a connection so that the order in which the connections are attempted is random.
See <<cluster>> for more information.
When using Publisher confirms and returns, the callbacks are now invoked on the connection factory's `executor`.
This avoids a possible deadlock in the `amqp-clients` library if you perform rabbit operations from within the callback.
See <<template-confirms>> for more information.
Also, the publisher confirm type is now specified with the `ConfirmType` enum instead of the two mutually exclusive setter methods.
The `RabbitConnectionFactoryBean` now uses TLS 1.2 by default when SSL is enabled.
See <<rabbitconnectionfactorybean-configuring-ssl>> for more information.
===== New MessagePostProcessor Classes
Classes `DeflaterPostProcessor` and `InflaterPostProcessor` were added to support compression and decompression, respectively, when the message content-encoding is set to `deflate`.
===== Other Changes
The `Declarables` object (for declaring multiple queues, exchanges, bindings) now has a filtered getter for each type.
See <<collection-declaration>> for more information.
You can now customize each `Declarable` bean before the `RabbitAdmin` processes the declaration thereof.
See <<automatic-declaration>> for more information.
`singleActiveConsumer()` has been added to the `QueueBuilder` to set the `x-single-active-consumer` queue argument.
See <<builder-api>> for more information.
Outbound headers with values of type `Class<?>` are now mapped using `getName()` instead of `toString()`.
See <<message-properties-converters>> for more information.
Recovery of failed producer-created batches is now supported.
See <<batch-retry>> for more information.
==== Changes in 2.1 Since 2.0
===== AMQP Client library

View File

@@ -13,6 +13,56 @@ Spring AMQP version 1.6 introduced the `spring-rabbit-test` jar, which provides
It is anticipated that this project will expand over time, but we need community feedback to make suggestions for the features needed to help with testing.
Please use https://jira.spring.io/browse/AMQP[JIRA] or https://github.com/spring-projects/spring-amqp/issues[GitHub Issues] to provide such feedback.
[[spring-rabbit-test]]
==== @SpringRabbitTest
Use this annotation to add infrastructure beans to the Spring test `ApplicationContext`.
This is not necessary when using, for example `@SpringBootTest` since Spring Boot's auto configuration will add the beans.
Beans that are registered are:
* `CachingConnectionFactory` (`autoConnectionFactory`). If `@RabbitEnabled` is present, its connectionn factory is used.
* `RabbitTemplate` (`autoRabbitTemplate`)
* `RabbitAdmin` (`autoRabbitAdmin`)
* `RabbitListenerContainerFactory` (`autoContainerFactory`)
In addition, the beans associated with `@EnableRabbit` (to support `@RabbitListener`) are added.
.Junit5 example
====
[source, java]
----
@SpringJunitConfig
@SpringRabbitTest
public class MyRabbitTests {
@Autowired
private RabbitTemplate template;
@Autowired
private RabbitAdmin admin;
@Autowired
private RabbitListenerEndpointRegistry registry;
@Test
void test() {
...
}
@Configuration
public static class Config {
...
}
}
----
====
With JUnit4, replace `@SpringJunitConfig` with `@RunWith(SpringRunnner.class)`.
[[mockito-answer]]
==== Mockito `Answer<?>` Implementations

View File

@@ -1,127 +1,12 @@
[[whats-new]]
== What's New
=== Changes in 2.2 Since 2.1
=== Changes in 2.3 Since 2.2
This section describes the changes between version 2.1 and version 2.2.
This section describes the changes between version 2.2 and version 2.3.
See <<change-history>> for changes in previous versions.
==== Package Changes
==== Testing Changes
The following classes/interfaces have been moved from `org.springframework.amqp.rabbit.core.support` to `org.springframework.amqp.rabbit.batch`:
* `BatchingStrategy`
* `MessageBatch`
* `SimpleBatchingStrategy`
In addition, `ListenerExecutionFailedException` has been moved from `org.springframework.amqp.rabbit.listener.exception` to `org.springframework.amqp.rabbit.support`.
==== Dependency Changes
JUnit (4) is now an optional dependency and will no longer appear as a transitive dependency.
The `spring-rabbit-junit` module is now a *compile* dependency in the `spring-rabbit-test` module for a better target application development experience when with only a single `spring-rabbit-test` we get the full stack of testing utilities for AMQP components.
==== "Breaking" API Changes
the JUnit (5) `RabbitAvailableCondition.getBrokerRunning()` now returns a `BrokerRunningSupport` instance instead of a `BrokerRunning`, which depends on JUnit 4.
It has the same API so it's just a matter of changing the class name of any references.
See <<junit5-conditions>> for more information.
==== ListenerContainer Changes
Messages with fatal exceptions are now rejected and NOT requeued, by default, even if the acknowledge mode is manual.
See <<exception-handling>> for more information.
Listener performance can now be monitored using Micrometer `Timer` s.
See <<micrometer>> for more information.
==== @RabbitListener Changes
You can now configure an `executor` on each listener, overriding the factory configuration, to more easily identify threads associated with the listener.
You can now override the container factory's `acknowledgeMode` property with the annotation's `ackMode` property.
See <<listener-property-overrides,overriding container factory properties>> for more information.
When using <<receiving-batch,batching>>, `@RabbitListener` methods can now receive a complete batch of messages in one call instead of getting them one-at-a-time.
When receiving batched messages one-at-a-time, the last message has the `isLastInBatch` message property set to true.
In addition, received batched messages now contain the `amqp_batchSize` header.
Listeners can also consume batches created in the `SimpleMessageListenerContainer`, even if the batch is not created by the producer.
See <<choose-container>> for more information.
Spring Data Projection interfaces are now supported by the `Jackson2JsonMessageConverter`.
See <<data-projection>> for more information.
The `Jackson2JsonMessageConverter` now assumes the content is JSON if there is no `contentType` property, or it is the default (`application/octet-string`).
See <<Jackson2JsonMessageConverter-from-message>> for more information.
Similarly. the `Jackson2XmlMessageConverter` now assumes the content is XML if there is no `contentType` property, or it is the default (`application/octet-string`).
See <<jackson2xml>> for more information.
When a `@RabbitListener` method returns a result, the bean and `Method` are now available in the reply message properties.
This allows configuration of a `beforeSendReplyMessagePostProcessor` to, for example, set a header in the reply to indicate which method was invoked on the server.
See <<async-annotation-driven-reply>> for more information.
You can now configure a `ReplyPostProcessor` to make modifications to a reply message before it is sent.
See <<async-annotation-driven-reply>> for more information.
==== AMQP Logging Appenders Changes
The Log4J and Logback `AmqpAppender` s now support a `verifyHostname` SSL option.
Also these appenders now can be configured to not add MDC entries as headers.
The `addMdcAsHeaders` boolean option has been introduces to configure such a behavior.
The appenders now support the `SaslConfig` property.
See <<logging>> for more information.
==== MessageListenerAdapter Changes
The `MessageListenerAdapter` provides now a new `buildListenerArguments(Object, Channel, Message)` method to build an array of arguments to be passed into target listener and an old one is deprecated.
See <<message-listener-adapter>> for more information.
==== Exchange/Queue Declaration Changes
The `ExchangeBuilder` and `QueueBuilder` fluent APIs used to create `Exchange` and `Queue` objects for declaration by `RabbitAdmin` now support "well known" arguments.
See <<builder-api>> for more information.
The `RabbitAdmin` has a new property `explicitDeclarationsOnly`.
See <<conditional-declaration>> for more information.
==== Connection Factory Changes
The `CachingConnectionFactory` has a new property `shuffleAddresses`.
When providing a list of broker node addresses, the list will be shuffled before creating a connection so that the order in which the connections are attempted is random.
See <<cluster>> for more information.
When using Publisher confirms and returns, the callbacks are now invoked on the connection factory's `executor`.
This avoids a possible deadlock in the `amqp-clients` library if you perform rabbit operations from within the callback.
See <<template-confirms>> for more information.
Also, the publisher confirm type is now specified with the `ConfirmType` enum instead of the two mutually exclusive setter methods.
The `RabbitConnectionFactoryBean` now uses TLS 1.2 by default when SSL is enabled.
See <<rabbitconnectionfactorybean-configuring-ssl>> for more information.
==== New MessagePostProcessor Classes
Classes `DeflaterPostProcessor` and `InflaterPostProcessor` were added to support compression and decompression, respectively, when the message content-encoding is set to `deflate`.
==== Other Changes
The `Declarables` object (for declaring multiple queues, exchanges, bindings) now has a filtered getter for each type.
See <<collection-declaration>> for more information.
You can now customize each `Declarable` bean before the `RabbitAdmin` processes the declaration thereof.
See <<automatic-declaration>> for more information.
`singleActiveConsumer()` has been added to the `QueueBuilder` to set the `x-single-active-consumer` queue argument.
See <<builder-api>> for more information.
Outbound headers with values of type `Class<?>` are now mapped using `getName()` instead of `toString()`.
See <<message-properties-converters>> for more information.
Recovery of failed producer-created batches is now supported.
See <<batch-retry>> for more information.
A new annotation `@SpringBootTest` is provided to automatically configure some infrastructure beans for when you are not using `SpringBootTest`.
See <<spring-rabbit-test>> for more information.