Re-instate lazy lookup for Binder in DestinationResolver

It's important not to depend on something heavyweight like a Binder
in a BeanPostProcessor. The usual trick (as implemented here) is
to do a lazy lookup of the thing that is eventually injected in the
post processes beans.

Fixes gh-82
This commit is contained in:
Dave Syer
2015-08-13 18:04:09 +01:00
committed by Marius Bogoevici
parent dafa0fdf40
commit 15e3c6066f
2 changed files with 44 additions and 20 deletions

View File

@@ -17,31 +17,26 @@
package org.springframework.cloud.stream.binder;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.integration.router.AbstractMappingMessageRouter;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.core.DestinationResolver;
/**
* A {@link BeanPostProcessor} that sets a {@link BinderAwareChannelResolver} on any bean of type
* {@link AbstractMappingMessageRouter} within the context.
*
*
* @author Mark Fisher
* @author Gary Russell
*/
public class BinderAwareRouterBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
public class BinderAwareRouterBeanPostProcessor implements BeanPostProcessor {
private final BinderAwareChannelResolver channelResolver;
private final DestinationResolver<MessageChannel> channelResolver;
public BinderAwareRouterBeanPostProcessor(BinderAwareChannelResolver channelResolver) {
public BinderAwareRouterBeanPostProcessor(DestinationResolver<MessageChannel> channelResolver) {
this.channelResolver = channelResolver;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
channelResolver.setBeanFactory(beanFactory);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
@@ -50,7 +45,7 @@ public class BinderAwareRouterBeanPostProcessor implements BeanPostProcessor, Be
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof AbstractMappingMessageRouter) {
((AbstractMappingMessageRouter) bean).setChannelResolver(channelResolver);
((AbstractMappingMessageRouter) bean).setChannelResolver(this.channelResolver);
}
return bean;
}

View File

@@ -38,6 +38,8 @@ import org.springframework.cloud.stream.endpoint.ChannelsEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.core.DestinationResolutionException;
import org.springframework.messaging.core.DestinationResolver;
/**
* Configuration class that provides necessary beans for {@link MessageChannel} binding.
@@ -61,8 +63,9 @@ public class ChannelBindingAdapterConfiguration {
@Bean
public ChannelBindingAdapter bindingAdapter() {
ChannelBindingAdapter adapter = new ChannelBindingAdapter(this.module, this.binder);
adapter.setChannelLocator(new DefaultChannelLocator(module));
ChannelBindingAdapter adapter = new ChannelBindingAdapter(this.module,
this.binder);
adapter.setChannelLocator(new DefaultChannelLocator(this.module));
adapter.setOutputChannels(getOutputChannels());
adapter.setInputChannels(getInputChannels());
adapter.setChannelResolver(binderAwareChannelResolver());
@@ -81,7 +84,8 @@ public class ChannelBindingAdapterConfiguration {
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition(name);
// for now, just assume that the beans are at least AbstractBeanDefinition
if (beanDefinition instanceof AbstractBeanDefinition
&& ((AbstractBeanDefinition) beanDefinition).getQualifier(Output.class.getName()) != null) {
&& ((AbstractBeanDefinition) beanDefinition)
.getQualifier(Output.class.getName()) != null) {
channels.add(new OutputChannelBinding(name));
}
}
@@ -95,7 +99,8 @@ public class ChannelBindingAdapterConfiguration {
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition(name);
// for now, just assume that the beans are at least AbstractBeanDefinition
if (beanDefinition instanceof AbstractBeanDefinition
&& ((AbstractBeanDefinition) beanDefinition).getQualifier(Input.class.getName()) != null) {
&& ((AbstractBeanDefinition) beanDefinition).getQualifier(Input.class
.getName()) != null) {
channels.add(new InputChannelBinding(name));
}
}
@@ -104,11 +109,35 @@ public class ChannelBindingAdapterConfiguration {
@Bean
public BinderAwareChannelResolver binderAwareChannelResolver() {
return new BinderAwareChannelResolver(BeanFactoryUtils.beanOfType(beanFactory, Binder.class), new Properties());
return new BinderAwareChannelResolver(this.binder, new Properties());
}
@Bean
public BinderAwareRouterBeanPostProcessor binderAwareRouterBeanPostProcessor(BinderAwareChannelResolver resolver) {
return new BinderAwareRouterBeanPostProcessor(resolver);
// IMPORTANT: Nested class to avoid instantiating all of the above early
@Configuration
protected static class PostProcessorConfiguration {
private BinderAwareChannelResolver binderAwareChannelResolver;
@Bean
public BinderAwareRouterBeanPostProcessor binderAwareRouterBeanPostProcessor(
final ConfigurableListableBeanFactory beanFactory) {
// IMPORTANT: Lazy delegate to avoid instantiating all of the above early
return new BinderAwareRouterBeanPostProcessor(
new DestinationResolver<MessageChannel>() {
@Override
public MessageChannel resolveDestination(String name)
throws DestinationResolutionException {
if (PostProcessorConfiguration.this.binderAwareChannelResolver == null) {
PostProcessorConfiguration.this.binderAwareChannelResolver = BeanFactoryUtils.beanOfType(
beanFactory, BinderAwareChannelResolver.class);
}
return PostProcessorConfiguration.this.binderAwareChannelResolver.resolveDestination(name);
}
});
}
}
}