Skip to content

Commit 2e25a25

Browse files
committed
Allow the auto-configured RabbitMQ ConnectionFactory to be customized
Closes gh-6719
1 parent a481b4a commit 2e25a25

File tree

4 files changed

+105
-7
lines changed

4 files changed

+105
-7
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2012-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.amqp;
18+
19+
import com.rabbitmq.client.ConnectionFactory;
20+
21+
/**
22+
* Callback interface that can be implemented by beans wishing to customize the
23+
* auto-configured RabbitMQ {@link ConnectionFactory}.
24+
*
25+
* @author Andy Wilkinson
26+
* @since 2.5.0
27+
*/
28+
@FunctionalInterface
29+
public interface ConnectionFactoryCustomizer {
30+
31+
/**
32+
* Customize the {@link ConnectionFactory}.
33+
* @param factory the factory to customize
34+
*/
35+
void customize(ConnectionFactory factory);
36+
37+
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfiguration.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -100,9 +100,13 @@ protected static class RabbitConnectionFactoryCreator {
100100
public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties properties,
101101
ResourceLoader resourceLoader, ObjectProvider<CredentialsProvider> credentialsProvider,
102102
ObjectProvider<CredentialsRefreshService> credentialsRefreshService,
103-
ObjectProvider<ConnectionNameStrategy> connectionNameStrategy) throws Exception {
104-
CachingConnectionFactory factory = new CachingConnectionFactory(getRabbitConnectionFactoryBean(properties,
105-
resourceLoader, credentialsProvider, credentialsRefreshService).getObject());
103+
ObjectProvider<ConnectionNameStrategy> connectionNameStrategy,
104+
ObjectProvider<ConnectionFactoryCustomizer> connectionFactoryCustomizers) throws Exception {
105+
com.rabbitmq.client.ConnectionFactory connectionFactory = getRabbitConnectionFactoryBean(properties,
106+
resourceLoader, credentialsProvider, credentialsRefreshService).getObject();
107+
connectionFactoryCustomizers.orderedStream()
108+
.forEach((customizer) -> customizer.customize(connectionFactory));
109+
CachingConnectionFactory factory = new CachingConnectionFactory(connectionFactory);
106110
PropertyMapper map = PropertyMapper.get();
107111
map.from(properties::determineAddresses).to(factory::setAddresses);
108112
map.from(properties::getAddressShuffleMode).whenNonNull().to(factory::setAddressShuffleMode);

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/amqp/RabbitAutoConfigurationTests.java

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,13 +26,15 @@
2626

2727
import com.rabbitmq.client.Address;
2828
import com.rabbitmq.client.Connection;
29+
import com.rabbitmq.client.JDKSaslConfig;
2930
import com.rabbitmq.client.SslContextFactory;
3031
import com.rabbitmq.client.TrustEverythingTrustManager;
3132
import com.rabbitmq.client.impl.CredentialsProvider;
3233
import com.rabbitmq.client.impl.CredentialsRefreshService;
3334
import com.rabbitmq.client.impl.DefaultCredentialsProvider;
3435
import org.aopalliance.aop.Advice;
3536
import org.junit.jupiter.api.Test;
37+
import org.mockito.InOrder;
3638

3739
import org.springframework.amqp.core.AcknowledgeMode;
3840
import org.springframework.amqp.core.AmqpAdmin;
@@ -60,6 +62,8 @@
6062
import org.springframework.context.annotation.Bean;
6163
import org.springframework.context.annotation.Configuration;
6264
import org.springframework.context.annotation.Primary;
65+
import org.springframework.core.Ordered;
66+
import org.springframework.core.annotation.Order;
6367
import org.springframework.retry.RetryPolicy;
6468
import org.springframework.retry.backoff.BackOffPolicy;
6569
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
@@ -75,6 +79,7 @@
7579
import static org.mockito.ArgumentMatchers.eq;
7680
import static org.mockito.ArgumentMatchers.isNull;
7781
import static org.mockito.BDDMockito.given;
82+
import static org.mockito.Mockito.inOrder;
7883
import static org.mockito.Mockito.mock;
7984
import static org.mockito.Mockito.verify;
8085

@@ -822,6 +827,29 @@ void whenMultipleCredentialsRefreshServiceAreAvailableThenConnectionFactoryHasNo
822827
.isNull());
823828
}
824829

830+
@Test
831+
void whenAConnectionFactoryCustomizerIsDefinedThenItCustomizesTheConnectionFactory() {
832+
this.contextRunner.withUserConfiguration(SaslConfigCustomizerConfiguration.class)
833+
.run((context) -> assertThat(getTargetConnectionFactory(context).getSaslConfig())
834+
.isInstanceOf(JDKSaslConfig.class));
835+
}
836+
837+
@Test
838+
void whenMultipleConnectionFactoryCustomizersAreDefinedThenTheyAreCalledInOrder() {
839+
this.contextRunner.withUserConfiguration(MultipleConnectionFactoryCustomizersConfiguration.class)
840+
.run((context) -> {
841+
ConnectionFactoryCustomizer firstCustomizer = context.getBean("firstCustomizer",
842+
ConnectionFactoryCustomizer.class);
843+
ConnectionFactoryCustomizer secondCustomizer = context.getBean("secondCustomizer",
844+
ConnectionFactoryCustomizer.class);
845+
InOrder inOrder = inOrder(firstCustomizer, secondCustomizer);
846+
com.rabbitmq.client.ConnectionFactory targetConnectionFactory = getTargetConnectionFactory(context);
847+
inOrder.verify(firstCustomizer).customize(targetConnectionFactory);
848+
inOrder.verify(secondCustomizer).customize(targetConnectionFactory);
849+
inOrder.verifyNoMoreInteractions();
850+
});
851+
}
852+
825853
private TrustManager getTrustManager(com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory) {
826854
SslContextFactory sslContextFactory = (SslContextFactory) ReflectionTestUtils.getField(rabbitConnectionFactory,
827855
"sslContextFactory");
@@ -1071,4 +1099,31 @@ CredentialsRefreshService credentialsRefreshService2() {
10711099

10721100
}
10731101

1102+
@Configuration(proxyBeanMethods = false)
1103+
static class SaslConfigCustomizerConfiguration {
1104+
1105+
@Bean
1106+
ConnectionFactoryCustomizer connectionFactoryCustomizer() {
1107+
return (connectionFactory) -> connectionFactory.setSaslConfig(new JDKSaslConfig(connectionFactory));
1108+
}
1109+
1110+
}
1111+
1112+
@Configuration(proxyBeanMethods = false)
1113+
static class MultipleConnectionFactoryCustomizersConfiguration {
1114+
1115+
@Bean
1116+
@Order(Ordered.LOWEST_PRECEDENCE)
1117+
ConnectionFactoryCustomizer secondCustomizer() {
1118+
return mock(ConnectionFactoryCustomizer.class);
1119+
}
1120+
1121+
@Bean
1122+
@Order(0)
1123+
ConnectionFactoryCustomizer firstCustomizer() {
1124+
return mock(ConnectionFactoryCustomizer.class);
1125+
}
1126+
1127+
}
1128+
10741129
}

spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5428,8 +5428,10 @@ Alternatively, you could configure the same connection using the `addresses` att
54285428
NOTE: When specifying addresses that way, the `host` and `port` properties are ignored.
54295429
If the address uses the `amqps` protocol, SSL support is enabled automatically.
54305430

5431-
If a `ConnectionNameStrategy` bean exists in the context, it will be automatically used to name connections created by the auto-configured `ConnectionFactory`.
5432-
See {spring-boot-autoconfigure-module-code}/amqp/RabbitProperties.java[`RabbitProperties`] for more of the supported options.
5431+
See {spring-boot-autoconfigure-module-code}/amqp/RabbitProperties.java[`RabbitProperties`] for more of the supported property-based configuration options.
5432+
To configure lower-level details of the RabbitMQ `ConnectionFactory` that is used by Spring AMQP, define a `ConnectionFactoryCustomizer` bean.
5433+
5434+
If a `ConnectionNameStrategy` bean exists in the context, it will be automatically used to name connections created by the auto-configured `CachingConnectionFactory`.
54335435

54345436
TIP: See https://spring.io/blog/2010/06/14/understanding-amqp-the-protocol-used-by-rabbitmq/[Understanding AMQP, the protocol used by RabbitMQ] for more details.
54355437

0 commit comments

Comments
 (0)