Skip to content

Commit b726974

Browse files
committed
Allow to easily customize ListenerContainerFactory
Previously, if one wants to create a custom `JmsListenerContainerFactory` or `RabbitListenerContainerFactory`, a bunch of code from the auto- configuration must be duplicated. This commit introduces two services to configure such factory for JMS and AMQP with the same sensible defaults that were applied by the auto-configufrations. Closes gh-5138
1 parent 1c170b3 commit b726974

File tree

6 files changed

+327
-57
lines changed

6 files changed

+327
-57
lines changed

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

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2015 the original author or authors.
2+
* Copyright 2012-2016 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.
@@ -20,7 +20,6 @@
2020
import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils;
2121
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
2222
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
23-
import org.springframework.boot.autoconfigure.amqp.RabbitProperties.Listener;
2423
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2524
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2625
import org.springframework.context.annotation.Bean;
@@ -37,30 +36,18 @@
3736
@ConditionalOnClass(EnableRabbit.class)
3837
class RabbitAnnotationDrivenConfiguration {
3938

39+
@Bean
40+
@ConditionalOnMissingBean
41+
public RabbitListenerContainerFactoryConfigurer rabbitListenerContainerFactoryConfigurer() {
42+
return new RabbitListenerContainerFactoryConfigurer();
43+
}
44+
4045
@Bean
4146
@ConditionalOnMissingBean(name = "rabbitListenerContainerFactory")
4247
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
43-
ConnectionFactory connectionFactory, RabbitProperties config) {
44-
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
45-
factory.setConnectionFactory(connectionFactory);
46-
Listener listenerConfig = config.getListener();
47-
factory.setAutoStartup(listenerConfig.isAutoStartup());
48-
if (listenerConfig.getAcknowledgeMode() != null) {
49-
factory.setAcknowledgeMode(listenerConfig.getAcknowledgeMode());
50-
}
51-
if (listenerConfig.getConcurrency() != null) {
52-
factory.setConcurrentConsumers(listenerConfig.getConcurrency());
53-
}
54-
if (listenerConfig.getMaxConcurrency() != null) {
55-
factory.setMaxConcurrentConsumers(listenerConfig.getMaxConcurrency());
56-
}
57-
if (listenerConfig.getPrefetch() != null) {
58-
factory.setPrefetchCount(listenerConfig.getPrefetch());
59-
}
60-
if (listenerConfig.getTransactionSize() != null) {
61-
factory.setTxSize(listenerConfig.getTransactionSize());
62-
}
63-
return factory;
48+
RabbitListenerContainerFactoryConfigurer configurer,
49+
ConnectionFactory connectionFactory) {
50+
return configurer.createRabbitListenerContainerFactory(connectionFactory);
6451
}
6552

6653
@EnableRabbit
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2012-2016 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+
* http://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 org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
20+
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
21+
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
22+
import org.springframework.beans.factory.annotation.Autowired;
23+
import org.springframework.util.Assert;
24+
25+
/**
26+
* Configure {@link RabbitListenerContainerFactory} with sensible defaults.
27+
*
28+
* @author Stephane Nicoll
29+
* @since 1.3.3
30+
*/
31+
public final class RabbitListenerContainerFactoryConfigurer {
32+
33+
private RabbitProperties rabbitProperties;
34+
35+
/**
36+
* Set the {@link RabbitProperties} to use.
37+
* @param rabbitProperties the {@link RabbitProperties}
38+
*/
39+
@Autowired
40+
public void setRabbitProperties(RabbitProperties rabbitProperties) {
41+
this.rabbitProperties = rabbitProperties;
42+
}
43+
44+
/**
45+
* Create a new and pre-configured {@link SimpleRabbitListenerContainerFactory} instance
46+
* for the specified {@link ConnectionFactory}.
47+
* @param connectionFactory the {@link ConnectionFactory} to use.
48+
* @return a pre-configured {@link SimpleRabbitListenerContainerFactory}
49+
*/
50+
public SimpleRabbitListenerContainerFactory createRabbitListenerContainerFactory(
51+
ConnectionFactory connectionFactory) {
52+
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
53+
configure(factory, connectionFactory);
54+
return factory;
55+
}
56+
57+
/**
58+
* Apply the default settings for the specified jms listener container factory. The
59+
* factory can be further tuned and default settings can be overridden.
60+
* @param factory the {@link SimpleRabbitListenerContainerFactory} instance to configure
61+
* @param connectionFactory the {@link ConnectionFactory} to use
62+
*/
63+
public void configure(SimpleRabbitListenerContainerFactory factory,
64+
ConnectionFactory connectionFactory) {
65+
Assert.notNull(factory, "Factory must not be null");
66+
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
67+
factory.setConnectionFactory(connectionFactory);
68+
RabbitProperties.Listener listenerConfig = this.rabbitProperties.getListener();
69+
factory.setAutoStartup(listenerConfig.isAutoStartup());
70+
if (listenerConfig.getAcknowledgeMode() != null) {
71+
factory.setAcknowledgeMode(listenerConfig.getAcknowledgeMode());
72+
}
73+
if (listenerConfig.getConcurrency() != null) {
74+
factory.setConcurrentConsumers(listenerConfig.getConcurrency());
75+
}
76+
if (listenerConfig.getMaxConcurrency() != null) {
77+
factory.setMaxConcurrentConsumers(listenerConfig.getMaxConcurrency());
78+
}
79+
if (listenerConfig.getPrefetch() != null) {
80+
factory.setPrefetchCount(listenerConfig.getPrefetch());
81+
}
82+
if (listenerConfig.getTransactionSize() != null) {
83+
factory.setTxSize(listenerConfig.getTransactionSize());
84+
}
85+
}
86+
87+
}

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2015 the original author or authors.
2+
* Copyright 2012-2016 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.
@@ -18,7 +18,6 @@
1818

1919
import javax.jms.ConnectionFactory;
2020

21-
import org.springframework.beans.factory.annotation.Autowired;
2221
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2322
import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi;
2423
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -29,7 +28,6 @@
2928
import org.springframework.jms.config.JmsListenerConfigUtils;
3029
import org.springframework.jms.support.destination.DestinationResolver;
3130
import org.springframework.jms.support.destination.JndiDestinationResolver;
32-
import org.springframework.transaction.jta.JtaTransactionManager;
3331

3432
/**
3533
* Configuration for Spring 4.1 annotation driven JMS.
@@ -42,41 +40,17 @@
4240
@ConditionalOnClass(EnableJms.class)
4341
class JmsAnnotationDrivenConfiguration {
4442

45-
@Autowired(required = false)
46-
private DestinationResolver destinationResolver;
47-
48-
@Autowired(required = false)
49-
private JtaTransactionManager transactionManager;
50-
51-
@Autowired
52-
private JmsProperties properties;
43+
@Bean
44+
@ConditionalOnMissingBean
45+
public JmsListenerContainerFactoryConfigurer jmsListenerContainerFactoryConfigurer() {
46+
return new JmsListenerContainerFactoryConfigurer();
47+
}
5348

5449
@Bean
5550
@ConditionalOnMissingBean(name = "jmsListenerContainerFactory")
5651
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
57-
ConnectionFactory connectionFactory) {
58-
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
59-
factory.setConnectionFactory(connectionFactory);
60-
factory.setPubSubDomain(this.properties.isPubSubDomain());
61-
if (this.transactionManager != null) {
62-
factory.setTransactionManager(this.transactionManager);
63-
}
64-
else {
65-
factory.setSessionTransacted(true);
66-
}
67-
if (this.destinationResolver != null) {
68-
factory.setDestinationResolver(this.destinationResolver);
69-
}
70-
JmsProperties.Listener listener = this.properties.getListener();
71-
factory.setAutoStartup(listener.isAutoStartup());
72-
if (listener.getAcknowledgeMode() != null) {
73-
factory.setSessionAcknowledgeMode(listener.getAcknowledgeMode().getMode());
74-
}
75-
String concurrency = listener.formatConcurrency();
76-
if (concurrency != null) {
77-
factory.setConcurrency(concurrency);
78-
}
79-
return factory;
52+
JmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
53+
return configurer.createJmsListenerContainerFactory(connectionFactory);
8054
}
8155

8256
@EnableJms
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2012-2016 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+
* http://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.jms;
18+
19+
import javax.jms.ConnectionFactory;
20+
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
23+
import org.springframework.jms.config.JmsListenerContainerFactory;
24+
import org.springframework.jms.support.destination.DestinationResolver;
25+
import org.springframework.transaction.jta.JtaTransactionManager;
26+
import org.springframework.util.Assert;
27+
28+
/**
29+
* Configure {@link JmsListenerContainerFactory} with sensible defaults.
30+
*
31+
* @author Stephane Nicoll
32+
* @since 1.3.3
33+
*/
34+
public final class JmsListenerContainerFactoryConfigurer {
35+
36+
private DestinationResolver destinationResolver;
37+
38+
private JtaTransactionManager transactionManager;
39+
40+
private JmsProperties jmsProperties;
41+
42+
/**
43+
* Set the {@link DestinationResolver} to use or {@code null} if no destination
44+
* resolver should be associated with the factory by default.
45+
* @param destinationResolver the {@link DestinationResolver}
46+
*/
47+
@Autowired(required = false)
48+
public void setDestinationResolver(DestinationResolver destinationResolver) {
49+
this.destinationResolver = destinationResolver;
50+
}
51+
52+
/**
53+
* Set the {@link JtaTransactionManager} to use or {@code null} if the JTA
54+
* support should not be used.
55+
* @param transactionManager the {@link JtaTransactionManager}
56+
*/
57+
@Autowired(required = false)
58+
public void setTransactionManager(JtaTransactionManager transactionManager) {
59+
this.transactionManager = transactionManager;
60+
}
61+
62+
/**
63+
* Set the {@link JmsProperties to use}.
64+
* @param jmsProperties the {@link JmsProperties}
65+
*/
66+
@Autowired
67+
public void setJmsProperties(JmsProperties jmsProperties) {
68+
this.jmsProperties = jmsProperties;
69+
}
70+
71+
/**
72+
* Create a new and pre-configured {@link DefaultJmsListenerContainerFactory} instance
73+
* for the specified {@link ConnectionFactory}.
74+
* @param connectionFactory the {@link ConnectionFactory} to use.
75+
* @return a pre-configured {@link DefaultJmsListenerContainerFactory}
76+
*/
77+
public DefaultJmsListenerContainerFactory createJmsListenerContainerFactory(
78+
ConnectionFactory connectionFactory) {
79+
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
80+
configure(factory, connectionFactory);
81+
return factory;
82+
}
83+
84+
/**
85+
* Apply the default settings for the specified jms listener container factory. The
86+
* factory can be further tuned and default settings can be overridden.
87+
* @param factory the {@link DefaultJmsListenerContainerFactory} instance to configure
88+
* @param connectionFactory the {@link ConnectionFactory} to use
89+
*/
90+
public void configure(DefaultJmsListenerContainerFactory factory,
91+
ConnectionFactory connectionFactory) {
92+
Assert.notNull(factory, "Factory must not be null");
93+
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
94+
factory.setConnectionFactory(connectionFactory);
95+
factory.setPubSubDomain(this.jmsProperties.isPubSubDomain());
96+
if (this.transactionManager != null) {
97+
factory.setTransactionManager(this.transactionManager);
98+
}
99+
else {
100+
factory.setSessionTransacted(true);
101+
}
102+
if (this.destinationResolver != null) {
103+
factory.setDestinationResolver(this.destinationResolver);
104+
}
105+
JmsProperties.Listener listener = this.jmsProperties.getListener();
106+
factory.setAutoStartup(listener.isAutoStartup());
107+
if (listener.getAcknowledgeMode() != null) {
108+
factory.setSessionAcknowledgeMode(listener.getAcknowledgeMode().getMode());
109+
}
110+
String concurrency = listener.formatConcurrency();
111+
if (concurrency != null) {
112+
factory.setConcurrency(concurrency);
113+
}
114+
}
115+
116+
}

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,21 @@ public void testDefaultContainerFactoryNoTransactionManager() {
212212
.getPropertyValue("transactionManager"));
213213
}
214214

215+
@Test
216+
public void testCustomContainerFactoryWithConfigurer() {
217+
this.context = doLoad(new Class<?>[]{TestConfiguration9.class,
218+
EnableJmsConfiguration.class}, "spring.jms.listener.autoStartup=false");
219+
assertTrue(this.context.containsBean("jmsListenerContainerFactory"));
220+
JmsListenerContainerFactory<?> jmsListenerContainerFactory = this.context.getBean(
221+
"customListenerContainerFactory", JmsListenerContainerFactory.class);
222+
assertEquals(DefaultJmsListenerContainerFactory.class,
223+
jmsListenerContainerFactory.getClass());
224+
DefaultMessageListenerContainer listenerContainer = ((DefaultJmsListenerContainerFactory) jmsListenerContainerFactory)
225+
.createListenerContainer(mock(JmsListenerEndpoint.class));
226+
assertEquals(DefaultMessageListenerContainer.CACHE_CONSUMER, listenerContainer.getCacheLevel());
227+
assertFalse(listenerContainer.isAutoStartup());
228+
}
229+
215230
@Test
216231
public void testPubSubDisabledByDefault() {
217232
load(TestConfiguration.class);
@@ -447,6 +462,21 @@ DataSourceTransactionManager transactionManager() {
447462

448463
}
449464

465+
@Configuration
466+
protected static class TestConfiguration9 {
467+
468+
@Bean
469+
JmsListenerContainerFactory<?> customListenerContainerFactory(
470+
JmsListenerContainerFactoryConfigurer configurer,
471+
ConnectionFactory connectionFactory) {
472+
DefaultJmsListenerContainerFactory factory = configurer
473+
.createJmsListenerContainerFactory(connectionFactory);
474+
factory.setCacheLevel(DefaultMessageListenerContainer.CACHE_CONSUMER);
475+
return factory;
476+
477+
}
478+
}
479+
450480
@Configuration
451481
@EnableJms
452482
protected static class EnableJmsConfiguration {

0 commit comments

Comments
 (0)