JmsListener/ScheduledAnnotationBeanPostProcessor uses SmartInitializingSingleton instead of ContextRefreshedEvent

Also reducing the container dependencies to BeanFactory instead of ApplicationContext, wherever possible.

Issue: SPR-12039
This commit is contained in:
Juergen Hoeller
2014-07-28 15:58:13 +02:00
parent b6389a6c66
commit 92c657e12d
10 changed files with 272 additions and 281 deletions

View File

@@ -22,14 +22,14 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.jms.config.DefaultJmsHandlerMethodFactory;
@@ -39,6 +39,7 @@ import org.springframework.jms.config.JmsListenerEndpointRegistrar;
import org.springframework.jms.config.JmsListenerEndpointRegistry;
import org.springframework.jms.config.MethodJmsListenerEndpoint;
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
@@ -60,6 +61,7 @@ import org.springframework.util.StringUtils;
* {@link EnableJms} Javadoc for complete usage details.
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @since 4.1
* @see JmsListener
* @see EnableJms
@@ -69,11 +71,11 @@ import org.springframework.util.StringUtils;
* @see org.springframework.jms.config.AbstractJmsListenerEndpoint
* @see MethodJmsListenerEndpoint
*/
public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered,
ApplicationContextAware, ApplicationListener<ContextRefreshedEvent> {
public class JmsListenerAnnotationBeanPostProcessor
implements BeanPostProcessor, Ordered, BeanFactoryAware, SmartInitializingSingleton {
/**
* The bean name of the default {@link JmsListenerContainerFactory}
* The bean name of the default {@link JmsListenerContainerFactory}.
*/
static final String DEFAULT_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME = "jmsListenerContainerFactory";
@@ -82,9 +84,9 @@ public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor
private String containerFactoryBeanName = DEFAULT_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME;
private final JmsHandlerMethodFactoryAdapter jmsHandlerMethodFactory = new JmsHandlerMethodFactoryAdapter();
private BeanFactory beanFactory;
private ApplicationContext applicationContext;
private final JmsHandlerMethodFactoryAdapter jmsHandlerMethodFactory = new JmsHandlerMethodFactoryAdapter();
private final JmsListenerEndpointRegistrar registrar = new JmsListenerEndpointRegistrar();
@@ -124,9 +126,50 @@ public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor
this.jmsHandlerMethodFactory.setJmsHandlerMethodFactory(jmsHandlerMethodFactory);
}
/**
* Making a {@link BeanFactory} available is optional; if not set,
* {@link JmsListenerConfigurer} beans won't get autodetected and an
* {@link #setEndpointRegistry endpoint registry} has to be explicitly configured.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void afterSingletonsInstantiated() {
this.registrar.setBeanFactory(this.beanFactory);
if (this.beanFactory instanceof ListableBeanFactory) {
Map<String, JmsListenerConfigurer> instances =
((ListableBeanFactory) this.beanFactory).getBeansOfType(JmsListenerConfigurer.class);
for (JmsListenerConfigurer configurer : instances.values()) {
configurer.configureJmsListeners(this.registrar);
}
}
if (this.registrar.getEndpointRegistry() == null) {
if (this.endpointRegistry == null) {
Assert.state(this.beanFactory != null, "BeanFactory must be set to find endpoint registry by bean name");
this.endpointRegistry = this.beanFactory.getBean(
AnnotationConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME, JmsListenerEndpointRegistry.class);
}
this.registrar.setEndpointRegistry(this.endpointRegistry);
}
if (this.containerFactoryBeanName != null) {
this.registrar.setContainerFactoryBeanName(this.containerFactoryBeanName);
}
// Set the custom handler method factory once resolved by the configurer
JmsHandlerMethodFactory handlerMethodFactory = this.registrar.getJmsHandlerMethodFactory();
if (handlerMethodFactory != null) {
this.jmsHandlerMethodFactory.setJmsHandlerMethodFactory(handlerMethodFactory);
}
// Actually register all listeners
this.registrar.afterPropertiesSet();
}
@@ -189,8 +232,9 @@ public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor
JmsListenerContainerFactory<?> factory = null;
String containerFactoryBeanName = jmsListener.containerFactory();
if (StringUtils.hasText(containerFactoryBeanName)) {
Assert.state(this.beanFactory != null, "BeanFactory must be set to obtain container factory by bean name");
try {
factory = this.applicationContext.getBean(containerFactoryBeanName, JmsListenerContainerFactory.class);
factory = this.beanFactory.getBean(containerFactoryBeanName, JmsListenerContainerFactory.class);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanInitializationException("Could not register jms listener endpoint on [" +
@@ -199,49 +243,7 @@ public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor
}
}
registrar.registerEndpoint(endpoint, factory);
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext() != this.applicationContext) {
return;
}
Map<String, JmsListenerConfigurer> instances =
this.applicationContext.getBeansOfType(JmsListenerConfigurer.class);
for (JmsListenerConfigurer configurer : instances.values()) {
configurer.configureJmsListeners(registrar);
}
this.registrar.setApplicationContext(this.applicationContext);
if (this.registrar.getEndpointRegistry() == null) {
if (this.endpointRegistry == null) {
this.endpointRegistry = this.applicationContext.getBean(
AnnotationConfigUtils.JMS_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME, JmsListenerEndpointRegistry.class);
}
this.registrar.setEndpointRegistry(this.endpointRegistry);
}
if (this.containerFactoryBeanName != null) {
this.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);
}
// Create all the listeners and starts them
try {
this.registrar.afterPropertiesSet();
}
catch (Exception ex) {
throw new BeanInitializationException("Failed to initialize JmsListenerEndpointRegistrar", ex);
}
this.registrar.registerEndpoint(endpoint, factory);
}
private String getEndpointId(JmsListener jmsListener) {
@@ -282,7 +284,7 @@ public class JmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor
private JmsHandlerMethodFactory createDefaultJmsHandlerMethodFactory() {
DefaultJmsHandlerMethodFactory defaultFactory = new DefaultJmsHandlerMethodFactory();
defaultFactory.setApplicationContext(applicationContext);
defaultFactory.setBeanFactory(beanFactory);
defaultFactory.afterPropertiesSet();
return defaultFactory;
}

View File

@@ -20,11 +20,10 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.convert.ConversionService;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.messaging.converter.GenericMessageConverter;
@@ -36,8 +35,6 @@ import org.springframework.messaging.handler.annotation.support.PayloadArgumentR
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolverComposite;
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
@@ -57,15 +54,13 @@ import org.springframework.validation.Validator;
* can be converted
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @since 4.1
* @see #setCustomArgumentResolvers(java.util.List)
* @see #setValidator(Validator)
* @see #setConversionService(ConversionService)
* @see #setConversionService
* @see #setValidator
* @see #setCustomArgumentResolvers
*/
public class DefaultJmsHandlerMethodFactory
implements JmsHandlerMethodFactory, InitializingBean, ApplicationContextAware {
private ApplicationContext applicationContext;
public class DefaultJmsHandlerMethodFactory implements JmsHandlerMethodFactory, BeanFactoryAware, InitializingBean {
private ConversionService conversionService = new DefaultFormattingConversionService();
@@ -73,22 +68,17 @@ public class DefaultJmsHandlerMethodFactory
private Validator validator = new NoOpValidator();
private List<HandlerMethodArgumentResolver> customArgumentResolvers
= new ArrayList<HandlerMethodArgumentResolver>();
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
private HandlerMethodArgumentResolverComposite argumentResolvers
= new HandlerMethodArgumentResolverComposite();
private final HandlerMethodArgumentResolverComposite argumentResolvers =
new HandlerMethodArgumentResolverComposite();
private BeanFactory beanFactory;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Set the {@link ConversionService} to use to convert the original
* message payload or headers.
*
* @see HeaderMethodArgumentResolver
* @see GenericMessageConverter
*/
@@ -96,24 +86,15 @@ public class DefaultJmsHandlerMethodFactory
this.conversionService = conversionService;
}
protected ConversionService getConversionService() {
return conversionService;
}
/**
* Set the {@link MessageConverter} to use. By default a {@link GenericMessageConverter}
* is used.
*
* @see GenericMessageConverter
*/
public void setMessageConverter(MessageConverter messageConverter) {
this.messageConverter = messageConverter;
}
protected MessageConverter getMessageConverter() {
return messageConverter;
}
/**
* Set the Validator instance used for validating @Payload arguments
* @see org.springframework.validation.annotation.Validated
@@ -123,27 +104,15 @@ public class DefaultJmsHandlerMethodFactory
this.validator = validator;
}
/**
* The configured Validator instance
*/
public Validator getValidator() {
return validator;
}
/**
* Set the list of custom {@code HandlerMethodArgumentResolver}s that will be used
* after resolvers for supported argument type.
* @param customArgumentResolvers the list of resolvers; never {@code null}.
* @param customArgumentResolvers the list of resolvers (never {@code null})
*/
public void setCustomArgumentResolvers(List<HandlerMethodArgumentResolver> customArgumentResolvers) {
Assert.notNull(customArgumentResolvers, "The 'customArgumentResolvers' cannot be null.");
this.customArgumentResolvers = customArgumentResolvers;
}
public List<HandlerMethodArgumentResolver> getCustomArgumentResolvers() {
return customArgumentResolvers;
}
/**
* Configure the complete list of supported argument types effectively overriding
* the ones configured by default. This is an advanced option. For most use cases
@@ -157,20 +126,26 @@ public class DefaultJmsHandlerMethodFactory
this.argumentResolvers.addResolvers(argumentResolvers);
}
public List<HandlerMethodArgumentResolver> getArgumentResolvers() {
return this.argumentResolvers.getResolvers();
/**
* A {@link BeanFactory} only needs to be available for placeholder resolution
* in handler method arguments; it's optional otherwise.
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void afterPropertiesSet() {
if (messageConverter == null) {
messageConverter = new GenericMessageConverter(getConversionService());
if (this.messageConverter == null) {
this.messageConverter = new GenericMessageConverter(this.conversionService);
}
if (this.argumentResolvers.getResolvers().isEmpty()) {
this.argumentResolvers.addResolvers(initArgumentResolvers());
}
}
@Override
public InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method) {
InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(bean, method);
@@ -179,27 +154,28 @@ public class DefaultJmsHandlerMethodFactory
}
protected List<HandlerMethodArgumentResolver> initArgumentResolvers() {
ConfigurableBeanFactory beanFactory =
(ClassUtils.isAssignableValue(ConfigurableApplicationContext.class, applicationContext)) ?
((ConfigurableApplicationContext) applicationContext).getBeanFactory() : null;
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
ConfigurableBeanFactory cbf = (this.beanFactory instanceof ConfigurableBeanFactory ?
(ConfigurableBeanFactory) this.beanFactory : null);
// Annotation-based argument resolution
resolvers.add(new HeaderMethodArgumentResolver(getConversionService(), beanFactory));
resolvers.add(new HeaderMethodArgumentResolver(this.conversionService, cbf));
resolvers.add(new HeadersMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new MessageMethodArgumentResolver());
resolvers.addAll(getCustomArgumentResolvers());
resolvers.add(new PayloadArgumentResolver(getMessageConverter(), getValidator()));
if (this.customArgumentResolvers != null) {
resolvers.addAll(this.customArgumentResolvers);
}
resolvers.add(new PayloadArgumentResolver(this.messageConverter, this.validator));
return resolvers;
}
private static final class NoOpValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return false;

View File

@@ -19,9 +19,9 @@ package org.springframework.jms.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
/**
@@ -29,24 +29,26 @@ import org.springframework.util.Assert;
* a {@link JmsListenerEndpointRegistry}.
*
* @author Stephane Nicoll
* @author Juergen Hoeller
* @since 4.1
* @see org.springframework.jms.annotation.JmsListenerConfigurer
*/
public class JmsListenerEndpointRegistrar implements ApplicationContextAware, InitializingBean {
public class JmsListenerEndpointRegistrar implements BeanFactoryAware, InitializingBean {
private JmsListenerEndpointRegistry endpointRegistry;
private String containerFactoryBeanName;
private JmsHandlerMethodFactory jmsHandlerMethodFactory;
private JmsListenerContainerFactory<?> containerFactory;
private JmsHandlerMethodFactory jmsHandlerMethodFactory;
private String containerFactoryBeanName;
private ApplicationContext applicationContext;
private BeanFactory beanFactory;
private final List<JmsListenerEndpointDescriptor> endpointDescriptors =
new ArrayList<JmsListenerEndpointDescriptor>();
/**
* Set the {@link JmsListenerEndpointRegistry} instance to use.
*/
@@ -59,27 +61,7 @@ public class JmsListenerEndpointRegistrar implements ApplicationContextAware, In
* registrar, may be {@code null}.
*/
public JmsListenerEndpointRegistry getEndpointRegistry() {
return endpointRegistry;
}
/**
* 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 setContainerFactoryBeanName(String containerFactoryBeanName) {
this.containerFactoryBeanName = containerFactoryBeanName;
}
/**
* 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 {@link #setContainerFactoryBeanName}.
*/
public void setContainerFactory(JmsListenerContainerFactory<?> containerFactory) {
this.containerFactory = containerFactory;
return this.endpointRegistry;
}
/**
@@ -98,12 +80,69 @@ public class JmsListenerEndpointRegistrar implements ApplicationContextAware, In
* Return the custom {@link JmsHandlerMethodFactory} to use, if any.
*/
public JmsHandlerMethodFactory getJmsHandlerMethodFactory() {
return jmsHandlerMethodFactory;
return this.jmsHandlerMethodFactory;
}
/**
* 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 {@link #setContainerFactoryBeanName}.
*/
public void setContainerFactory(JmsListenerContainerFactory<?> containerFactory) {
this.containerFactory = containerFactory;
}
/**
* 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)}.
* @see #setBeanFactory
*/
public void setContainerFactoryBeanName(String containerFactoryBeanName) {
this.containerFactoryBeanName = containerFactoryBeanName;
}
/**
* A {@link BeanFactory} only needs to be available in conjunction with
* {@link #setContainerFactoryBeanName}.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void afterPropertiesSet() {
registerAllEndpoints();
}
protected void registerAllEndpoints() {
for (JmsListenerEndpointDescriptor descriptor : this.endpointDescriptors) {
this.endpointRegistry.registerListenerContainer(descriptor.endpoint, resolveContainerFactory(descriptor));
}
}
private JmsListenerContainerFactory<?> resolveContainerFactory(JmsListenerEndpointDescriptor descriptor) {
if (descriptor.containerFactory != null) {
return descriptor.containerFactory;
}
else if (this.containerFactory != null) {
return this.containerFactory;
}
else if (this.containerFactoryBeanName != null) {
Assert.state(this.beanFactory != null, "BeanFactory must be set to obtain container factory by bean name");
this.containerFactory = this.beanFactory.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 " +
JmsListenerContainerFactory.class.getSimpleName() + " to use for [" +
descriptor.endpoint + "] no factory was given and no default is set.");
}
}
/**
@@ -129,38 +168,6 @@ public class JmsListenerEndpointRegistrar implements ApplicationContextAware, In
registerEndpoint(endpoint, null);
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(applicationContext, "ApplicationContext must not be null");
startAllEndpoints();
}
protected void startAllEndpoints() throws Exception {
for (JmsListenerEndpointDescriptor descriptor : endpointDescriptors) {
endpointRegistry.registerListenerContainer(
descriptor.endpoint, resolveContainerFactory(descriptor));
}
}
private JmsListenerContainerFactory<?> resolveContainerFactory(JmsListenerEndpointDescriptor descriptor) {
if (descriptor.containerFactory != null) {
return descriptor.containerFactory;
}
else if (this.containerFactory != null) {
return this.containerFactory;
}
else if (this.containerFactoryBeanName != null) {
this.containerFactory = this.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 " +
JmsListenerContainerFactory.class.getSimpleName() + " to use for [" +
descriptor.endpoint + "] no factory was given and no default is set.");
}
}
private static class JmsListenerEndpointDescriptor {