INT-3701: Assist Boot with GPFB Type Determination

JIRA: https://jira.spring.io/browse/INT-3701

Allow Spring Boot to determine type created by GatewayProxyFactoryBean

Spring Boot's @ConditionalOnBean can be used to activate some
configuration when a bean of a particular type is present in the
application context. To avoid early initialization, the search for
beans is performed without instantiating them, i.e. it relies on the
information that's available from the bean's class and its bean
definition. This causes a problem with GatewayProxyFactoryBean as its
a FactoryBean<Object> so Boot's best guess is that the bean that's
produced by the factory will be an Object. For cases where the bean's
type signiture does not contain enougn information to determine its
type,  Boot looks at an attribute, factoryBeanObjectType, on the
factory bean's definition. The value of this attribute can be a Class
or a String class name.

This commit updates MessagingGatewayRegistrar to set the
value of factoryBeanObjectType attribute to be the configured
service interface for the gateway.
This commit is contained in:
Andy Wilkinson
2015-04-16 10:04:34 +01:00
committed by Gary Russell
parent 15150ac499
commit 2653ce9aed
4 changed files with 33 additions and 8 deletions

View File

@@ -34,6 +34,9 @@ public final class IntegrationConfigUtils {
public static final String HANDLER_ALIAS_SUFFIX = ".handler";
// TODO: Boot constant - move to Spring Framework?
public static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
public static void registerSpelFunctionBean(BeanDefinitionRegistry registry, String functionId, String className,
String methodSignature) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SpelFunctionFactoryBean.class)

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2015 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.
@@ -23,12 +23,11 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanMetadataAttribute;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
@@ -53,12 +52,11 @@ import org.springframework.util.StringUtils;
*
* @author Artem Bilan
* @author Gary Russell
* @author Andy Wilksinson
* @since 4.0
*/
public class MessagingGatewayRegistrar implements ImportBeanDefinitionRegistrar {
private static final Log logger = LogFactory.getLog(MessagingGatewayRegistrar.class);
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (importingClassMetadata != null && importingClassMetadata.isAnnotated(MessagingGateway.class.getName())) {
@@ -163,7 +161,11 @@ public class MessagingGatewayRegistrar implements ImportBeanDefinitionRegistrar
gatewayProxyBuilder.addConstructorArgValue(serviceInterface);
return new BeanDefinitionHolder(gatewayProxyBuilder.getBeanDefinition(), id);
AbstractBeanDefinition beanDefinition = gatewayProxyBuilder.getBeanDefinition();
beanDefinition.addMetadataAttribute(new BeanMetadataAttribute(IntegrationConfigUtils.FACTORY_BEAN_OBJECT_TYPE,
serviceInterface));
return new BeanDefinitionHolder(beanDefinition, id);
}
/**

View File

@@ -49,7 +49,7 @@
reactor-environment="reactorEnvironment"/>
<!-- no assertions for this. The fact that this config does not result in error is sufficient -->
<gateway default-request-channel="nullChannel"/>
<gateway id="defaultConfig" default-request-channel="nullChannel"/>
<beans:bean id="testExecutor" class="org.springframework.integration.config.xml.GatewayParserTests$TestExecutor"/>

View File

@@ -30,8 +30,12 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.integration.config.IntegrationConfigUtils;
import org.springframework.integration.gateway.RequestReplyExchanger;
import org.springframework.integration.gateway.TestService;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
@@ -105,6 +109,22 @@ public class GatewayParserTests {
assertNull(TestUtils.getPropertyValue(service, "asyncExecutor"));
}
@Test
public void testFactoryBeanObjectTypeWithServiceInterface() throws Exception {
ConfigurableListableBeanFactory beanFactory = ((GenericApplicationContext)context).getBeanFactory();
Object attribute = beanFactory.getMergedBeanDefinition("&oneWay").getAttribute(
IntegrationConfigUtils.FACTORY_BEAN_OBJECT_TYPE);
assertEquals(TestService.class.getName(), attribute);
}
@Test
public void testFactoryBeanObjectTypeWithNoServiceInterface() throws Exception {
ConfigurableListableBeanFactory beanFactory = ((GenericApplicationContext)context).getBeanFactory();
Object attribute = beanFactory.getMergedBeanDefinition("&defaultConfig").getAttribute(
IntegrationConfigUtils.FACTORY_BEAN_OBJECT_TYPE);
assertEquals(RequestReplyExchanger.class.getName(), attribute);
}
@Test
public void testPromiseGateway() throws Exception {
PollableChannel requestChannel = context.getBean("requestChannel", PollableChannel.class);