Default JmsListenerContainerFactory lookup

Prior to this commit, the default JmsListenerContainerFactory to use
must be explicitly set. Since having a single container factory is a
fairly common use case, we look up the default one automatically
using the bean name "jmsListenerContainerFactory".

It is still possible to provide an explicit default but since it refers
more to "the" container factory to use, the parameter has been
renamed to "containerFactory" which is shorter and more explicit.

The lookup strategy is lazy: if all endpoints are providing an
explicit container factory and no container factory with the
"jmsListenerContainerFactory" bean name exists, no exception
will be thrown.

Issue : SPR-11706
This commit is contained in:
Stephane Nicoll
2014-04-24 10:31:44 +03:00
parent 08f0395033
commit 4b0aba63df
15 changed files with 195 additions and 81 deletions

View File

@@ -66,7 +66,9 @@ import org.springframework.context.annotation.Import;
* }</pre>
*
* The container factory to use is identified by the {@link JmsListener#containerFactory() containerFactory}
* attribute defining the name of the {@code JmsListenerContainerFactory} bean to use.
* attribute defining the name of the {@code JmsListenerContainerFactory} bean to use. When none
* is set a {@code JmsListenerContainerFactory} bean with name {@code jmsListenerContainerFactory} is
* assumed to be present.
*
* <p>the following configuration would ensure that every time a {@link javax.jms.Message}
* is received on the {@link javax.jms.Destination} named "myQueue", {@code MyService.process()}
@@ -118,9 +120,8 @@ import org.springframework.context.annotation.Import;
* <p>When more control is desired, a {@code @Configuration} class may implement
* {@link JmsListenerConfigurer}. This allows access to the underlying
* {@link org.springframework.jms.config.JmsListenerEndpointRegistrar JmsListenerEndpointRegistrar}
* instance. The following example demonstrates how to specify a default
* {@code JmsListenerContainerFactory} so that {@link JmsListener#containerFactory()} may be
* omitted for endpoints willing to use the <em>default</em> container factory.
* instance. The following example demonstrates how to specify an explicit default
* {@code JmsListenerContainerFactory}
*
* <pre class="code">
* &#064;Configuration
@@ -128,7 +129,7 @@ import org.springframework.context.annotation.Import;
* public class AppConfig implements JmsListenerConfigurer {
* &#064;Override
* public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
* registrar.setDefaultContainerFactory(myJmsListenerContainerFactory());
* registrar.setContainerFactory(myJmsListenerContainerFactory());
* }
*
* &#064;Bean
@@ -146,7 +147,7 @@ import org.springframework.context.annotation.Import;
* configuration:
* <pre class="code">
* {@code <beans>
* <jms:annotation-driven default-container-factory="myJmsListenerContainerFactory"/>
* <jms:annotation-driven container-factory="myJmsListenerContainerFactory"/>
*
* <bean id="myJmsListenerContainerFactory"
* class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">

View File

@@ -28,8 +28,10 @@ import org.springframework.messaging.handler.annotation.MessageMapping;
* Annotation that marks a method to be the target of a JMS message
* listener on the specified {@link #destination()}. The {@link #containerFactory()}
* identifies the {@link org.springframework.jms.config.JmsListenerContainerFactory
* JmsListenerContainerFactory} to use to build the jms listener container. It may
* be omitted as long as a <em>default</em> container factory has been defined.
* JmsListenerContainerFactory} to use to build the jms listener container. If not
* set, a <em>default</em> container factory is assumed to be available with a bean
* name of {@code jmsListenerContainerFactory} unless an explicit default has been
* provided through configuration.
*
* <p>Processing of {@code @JmsListener} annotations is performed by
* registering a {@link JmsListenerAnnotationBeanPostProcessor}. This can be

View File

@@ -73,13 +73,18 @@ import org.springframework.util.StringUtils;
public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered,
ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
/**
* The bean name of the default {@link JmsListenerContainerFactory}
*/
static final String DEFAULT_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME = "jmsListenerContainerFactory";
private final AtomicInteger counter = new AtomicInteger();
private ApplicationContext applicationContext;
private JmsListenerEndpointRegistry endpointRegistry;
private JmsListenerContainerFactory<?> defaultContainerFactory;
private String containerFactoryBeanName = DEFAULT_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME;
private final JmsHandlerMethodFactoryAdapter jmsHandlerMethodFactory = new JmsHandlerMethodFactoryAdapter();
@@ -99,12 +104,12 @@ public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor
}
/**
* Set the default {@link JmsListenerContainerFactory} to use in case a
* {@link JmsListener} does not define any.
* {@linkplain JmsListener#containerFactory() containerFactory}
* Set the name of the {@link JmsListenerContainerFactory} to use by default.
* <p/>If none is specified, {@value #DEFAULT_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME}
* is assumed to be defined.
*/
public void setDefaultContainerFactory(JmsListenerContainerFactory<?> defaultContainerFactory) {
this.defaultContainerFactory = defaultContainerFactory;
public void setContainerFactoryBeanName(String containerFactoryBeanName) {
this.containerFactoryBeanName = containerFactoryBeanName;
}
/**
@@ -209,6 +214,9 @@ public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor
for (JmsListenerConfigurer configurer : instances.values()) {
configurer.configureJmsListeners(registrar);
}
registrar.setApplicationContext(this.applicationContext);
if (registrar.getEndpointRegistry() == null) {
if (endpointRegistry == null) {
endpointRegistry = applicationContext
@@ -217,10 +225,13 @@ public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor
}
registrar.setEndpointRegistry(endpointRegistry);
}
if (registrar.getDefaultContainerFactory() == null && defaultContainerFactory != null) {
registrar.setDefaultContainerFactory(defaultContainerFactory);
if (this.containerFactoryBeanName != null) {
registrar.setContainerFactoryBeanName(this.containerFactoryBeanName);
}
// Set the custom handler method factory once resolved by the configurer
JmsHandlerMethodFactory handlerMethodFactory = registrar.getJmsHandlerMethodFactory();
if (handlerMethodFactory != null) {
this.jmsHandlerMethodFactory.setJmsHandlerMethodFactory(handlerMethodFactory);
@@ -235,7 +246,6 @@ public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor
}
}
private String getEndpointId(JmsListener jmsListener) {
if (StringUtils.hasText(jmsListener.id())) {
return jmsListener.id();
@@ -267,7 +277,7 @@ public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor
private JmsHandlerMethodFactory getJmsHandlerMethodFactory() {
if (jmsHandlerMethodFactory == null) {
jmsHandlerMethodFactory= createDefaultJmsHandlerMethodFactory();
jmsHandlerMethodFactory = createDefaultJmsHandlerMethodFactory();
}
return jmsHandlerMethodFactory;
}

View File

@@ -64,10 +64,12 @@ final class AnnotationDrivenJmsBeanDefinitionParser implements BeanDefinitionPar
else {
registerDefaultEndpointRegistry(source, parserContext);
}
String defaultContainerFactory = element.getAttribute("default-container-factory");
if (StringUtils.hasText(defaultContainerFactory)) {
builder.addPropertyReference("defaultContainerFactory", defaultContainerFactory);
String containerFactory = element.getAttribute("container-factory");
if (StringUtils.hasText(containerFactory)) {
builder.addPropertyValue("containerFactoryBeanName", containerFactory);
}
String handlerMethodFactory = element.getAttribute("handler-method-factory");
if (StringUtils.hasText(handlerMethodFactory)) {
builder.addPropertyReference("jmsHandlerMethodFactory", handlerMethodFactory);

View File

@@ -20,6 +20,8 @@ import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
/**
@@ -30,14 +32,18 @@ import org.springframework.util.Assert;
* @since 4.1
* @see org.springframework.jms.annotation.JmsListenerConfigurer
*/
public class JmsListenerEndpointRegistrar implements InitializingBean {
public class JmsListenerEndpointRegistrar implements ApplicationContextAware, InitializingBean {
private JmsListenerEndpointRegistry endpointRegistry;
private JmsListenerContainerFactory<?> defaultContainerFactory;
private String containerFactoryBeanName;
private JmsListenerContainerFactory<?> containerFactory;
private JmsHandlerMethodFactory jmsHandlerMethodFactory;
private ApplicationContext applicationContext;
private final List<JmsListenerEndpointDescriptor> endpointDescriptors
= new ArrayList<JmsListenerEndpointDescriptor>();
@@ -57,20 +63,24 @@ public class JmsListenerEndpointRegistrar implements InitializingBean {
}
/**
* Set the default {@link JmsListenerContainerFactory} to use in case a
* {@link JmsListenerEndpoint} is registered with a {@code null} container
* factory.
* Set the bean name of the {@link JmsListenerContainerFactory} to use in
* case a {@link JmsListenerEndpoint} is registered with a {@code null}
* container factory. Alternatively, the container factory instance can
* be registered directly, see {@link #setContainerFactory(JmsListenerContainerFactory)}
*/
public void setDefaultContainerFactory(JmsListenerContainerFactory<?> defaultContainerFactory) {
this.defaultContainerFactory = defaultContainerFactory;
public void setContainerFactoryBeanName(String containerFactoryBeanName) {
this.containerFactoryBeanName = containerFactoryBeanName;
}
/**
* Return the {@link JmsListenerContainerFactory} to use if none has been
* defined for a particular endpoint or {@code null} if no default is set.
* Set the {@link JmsListenerContainerFactory} to use in case a
* {@link JmsListenerEndpoint} is registered with a {@code null} container
* factory.
* <p>Alternatively, the bean name of the {@link JmsListenerContainerFactory}
* to use can be specified for a lazy lookup, see {@see #setContainerFactoryBeanName}
*/
public JmsListenerContainerFactory<?> getDefaultContainerFactory() {
return defaultContainerFactory;
public void setContainerFactory(JmsListenerContainerFactory<?> containerFactory) {
this.containerFactory = containerFactory;
}
/**
@@ -92,6 +102,11 @@ public class JmsListenerEndpointRegistrar implements InitializingBean {
return jmsHandlerMethodFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Register a new {@link JmsListenerEndpoint} alongside the {@link JmsListenerContainerFactory}
* to use to create the underlying container.
@@ -109,7 +124,7 @@ public class JmsListenerEndpointRegistrar implements InitializingBean {
* Register a new {@link JmsListenerEndpoint} using the default {@link JmsListenerContainerFactory}
* to create the underlying container.
*
* @see #setDefaultContainerFactory(JmsListenerContainerFactory)
* @see #setContainerFactory(JmsListenerContainerFactory)
* @see #registerEndpoint(JmsListenerEndpoint, JmsListenerContainerFactory)
*/
public void registerEndpoint(JmsListenerEndpoint endpoint) {
@@ -118,6 +133,7 @@ public class JmsListenerEndpointRegistrar implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(applicationContext, "ApplicationContext must not be null");
startAllEndpoints();
}
@@ -132,8 +148,13 @@ public class JmsListenerEndpointRegistrar implements InitializingBean {
if (descriptor.containerFactory != null) {
return descriptor.containerFactory;
}
else if (defaultContainerFactory != null) {
return defaultContainerFactory;
else if (this.containerFactory != null) {
return this.containerFactory;
}
else if (this.containerFactoryBeanName != null) {
this.containerFactory = applicationContext.getBean(
this.containerFactoryBeanName, JmsListenerContainerFactory.class);
return this.containerFactory; // Consider changing this if live change of the factory is required
}
else {
throw new IllegalStateException("Could not resolve the "