From d13b9a98c5192a4bd2f0cf1d375fd88f3720e7fc Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 29 Feb 2016 17:33:34 +0100 Subject: [PATCH] Auto-configure JMS MessageConverter If a `MessageConverter` bean is available, we now associate it to the created `JmsTemplate` and `JmsListenerContainerFactory`. That way, registering a custom `MessageConverter` is all that's needed. The JMS auto-configuration is now using the new `ObjectProvider` that offers a nicer API to detect if a primary candidate is available for optional collaborators. Closes gh-4282 --- ...JmsListenerContainerFactoryConfigurer.java | 16 ++++++++ .../jms/JmsAnnotationDrivenConfiguration.java | 18 +++++--- .../jms/JmsAutoConfiguration.java | 16 ++++++-- .../jms/JmsAutoConfigurationTests.java | 41 +++++++++++++++++++ .../main/asciidoc/spring-boot-features.adoc | 7 +++- 5 files changed, 86 insertions(+), 12 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java index d666ce514b..5e74996be9 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/DefaultJmsListenerContainerFactoryConfigurer.java @@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.jms; import javax.jms.ConnectionFactory; import org.springframework.jms.config.DefaultJmsListenerContainerFactory; +import org.springframework.jms.support.converter.MessageConverter; import org.springframework.jms.support.destination.DestinationResolver; import org.springframework.transaction.jta.JtaTransactionManager; import org.springframework.util.Assert; @@ -33,6 +34,8 @@ public final class DefaultJmsListenerContainerFactoryConfigurer { private DestinationResolver destinationResolver; + private MessageConverter messageConverter; + private JtaTransactionManager transactionManager; private JmsProperties jmsProperties; @@ -46,6 +49,16 @@ public final class DefaultJmsListenerContainerFactoryConfigurer { this.destinationResolver = destinationResolver; } + /** + * Set the {@link MessageConverter} to use or {@code null} if the out-of-the-box + * converter should be used. + * @param messageConverter the {@link MessageConverter} + * @since 1.4.0 + */ + public void setMessageConverter(MessageConverter messageConverter) { + this.messageConverter = messageConverter; + } + /** * Set the {@link JtaTransactionManager} to use or {@code null} if the JTA support * should not be used. @@ -84,6 +97,9 @@ public final class DefaultJmsListenerContainerFactoryConfigurer { if (this.destinationResolver != null) { factory.setDestinationResolver(this.destinationResolver); } + if (this.messageConverter != null) { + factory.setMessageConverter(this.messageConverter); + } JmsProperties.Listener listener = this.jmsProperties.getListener(); factory.setAutoStartup(listener.isAutoStartup()); if (listener.getAcknowledgeMode() != null) { diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java index 66d3a17afa..6eb600c5ce 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.java @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.jms; import javax.jms.ConnectionFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi; @@ -27,6 +28,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.jms.annotation.EnableJms; import org.springframework.jms.config.DefaultJmsListenerContainerFactory; import org.springframework.jms.config.JmsListenerConfigUtils; +import org.springframework.jms.support.converter.MessageConverter; import org.springframework.jms.support.destination.DestinationResolver; import org.springframework.jms.support.destination.JndiDestinationResolver; import org.springframework.transaction.jta.JtaTransactionManager; @@ -42,11 +44,14 @@ import org.springframework.transaction.jta.JtaTransactionManager; @ConditionalOnClass(EnableJms.class) class JmsAnnotationDrivenConfiguration { - @Autowired(required = false) - private DestinationResolver destinationResolver; + @Autowired + private ObjectProvider destinationResolver; + + @Autowired + private ObjectProvider transactionManager; - @Autowired(required = false) - private JtaTransactionManager transactionManager; + @Autowired + private ObjectProvider messageConverter; @Autowired private JmsProperties properties; @@ -55,8 +60,9 @@ class JmsAnnotationDrivenConfiguration { @ConditionalOnMissingBean public DefaultJmsListenerContainerFactoryConfigurer jmsListenerContainerFactoryConfigurer() { DefaultJmsListenerContainerFactoryConfigurer configurer = new DefaultJmsListenerContainerFactoryConfigurer(); - configurer.setDestinationResolver(this.destinationResolver); - configurer.setTransactionManager(this.transactionManager); + configurer.setDestinationResolver(this.destinationResolver.getIfUnique()); + configurer.setTransactionManager(this.transactionManager.getIfUnique()); + configurer.setMessageConverter(this.messageConverter.getIfUnique()); configurer.setJmsProperties(this.properties); return configurer; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfiguration.java index d5b2385427..555baeaf60 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014 the original author or authors. + * Copyright 2012-2016 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. @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.jms; import javax.jms.ConnectionFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; @@ -29,6 +30,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.jms.core.JmsTemplate; +import org.springframework.jms.support.converter.MessageConverter; import org.springframework.jms.support.destination.DestinationResolver; /** @@ -50,8 +52,11 @@ public class JmsAutoConfiguration { @Autowired private ConnectionFactory connectionFactory; - @Autowired(required = false) - private DestinationResolver destinationResolver; + @Autowired + private ObjectProvider destinationResolver; + + @Autowired + private ObjectProvider messageConverter; @Bean @ConditionalOnMissingBean @@ -59,7 +64,10 @@ public class JmsAutoConfiguration { JmsTemplate jmsTemplate = new JmsTemplate(this.connectionFactory); jmsTemplate.setPubSubDomain(this.properties.isPubSubDomain()); if (this.destinationResolver != null) { - jmsTemplate.setDestinationResolver(this.destinationResolver); + jmsTemplate.setDestinationResolver(this.destinationResolver.getIfUnique()); + } + if (this.messageConverter != null) { + jmsTemplate.setMessageConverter(this.messageConverter.getIfUnique()); } return jmsTemplate; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java index d4b337cac1..b608c45ca9 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jms/JmsAutoConfigurationTests.java @@ -32,6 +32,7 @@ import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jms.annotation.EnableJms; import org.springframework.jms.config.DefaultJmsListenerContainerFactory; @@ -42,6 +43,7 @@ import org.springframework.jms.config.SimpleJmsListenerContainerFactory; import org.springframework.jms.core.JmsMessagingTemplate; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.listener.DefaultMessageListenerContainer; +import org.springframework.jms.support.converter.MessageConverter; import org.springframework.transaction.jta.JtaTransactionManager; import static org.assertj.core.api.Assertions.assertThat; @@ -202,6 +204,20 @@ public class JmsAutoConfigurationTests { .getPropertyValue("transactionManager")).isNull(); } + @Test + public void testDefaultContainerFactoryWithMessageConverters() { + this.context = createContext(MessageConvertersConfiguration.class, + EnableJmsConfiguration.class); + JmsListenerContainerFactory jmsListenerContainerFactory = this.context.getBean( + "jmsListenerContainerFactory", JmsListenerContainerFactory.class); + assertThat(jmsListenerContainerFactory.getClass()) + .isEqualTo(DefaultJmsListenerContainerFactory.class); + DefaultMessageListenerContainer listenerContainer = ((DefaultJmsListenerContainerFactory) jmsListenerContainerFactory) + .createListenerContainer(mock(JmsListenerEndpoint.class)); + assertThat(listenerContainer.getMessageConverter()) + .isSameAs(this.context.getBean("myMessageConverter")); + } + @Test public void testCustomContainerFactoryWithConfigurer() { this.context = doLoad( @@ -219,6 +235,15 @@ public class JmsAutoConfigurationTests { assertThat(listenerContainer.isAutoStartup()).isFalse(); } + + @Test + public void testJmsTemplateWithMessageConverters() { + load(MessageConvertersConfiguration.class); + JmsTemplate jmsTemplate = this.context.getBean(JmsTemplate.class); + assertThat(jmsTemplate.getMessageConverter()).isSameAs( + this.context.getBean("myMessageConverter")); + } + @Test public void testPubSubDisabledByDefault() { load(TestConfiguration.class); @@ -452,6 +477,22 @@ public class JmsAutoConfigurationTests { } + @Configuration + protected static class MessageConvertersConfiguration { + + @Bean + @Primary + public MessageConverter myMessageConverter() { + return mock(MessageConverter.class); + } + + @Bean + public MessageConverter anotherMessageConverter() { + return mock(MessageConverter.class); + } + + } + @Configuration protected static class TestConfiguration9 { diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 33c0be8b7e..189afdb0cd 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -3590,7 +3590,9 @@ beans: ---- NOTE: {spring-javadoc}/jms/core/JmsMessagingTemplate.{dc-ext}[`JmsMessagingTemplate`] -can be injected in a similar manner. +can be injected in a similar manner. If a `DestinationResolver` or `MessageConverter` +beans are defined, they are associated automatically to the auto-configured +`JmsTemplate`. @@ -3599,7 +3601,8 @@ can be injected in a similar manner. When the JMS infrastructure is present, any bean can be annotated with `@JmsListener` to create a listener endpoint. If no `JmsListenerContainerFactory` has been defined, a -default one is configured automatically. +default one is configured automatically. If a `DestinationResolver` or `MessageConverter` +beans are defined, they are associated automatically to the default factory. The default factory is transactional by default. If you are running in an infrastructure where a `JtaTransactionManager` is present, it will be associated to the listener container