diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/GatewayProxyInstantiationPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/GatewayProxyInstantiationPostProcessor.java new file mode 100644 index 0000000000..07d596b05f --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/GatewayProxyInstantiationPostProcessor.java @@ -0,0 +1,75 @@ +/* + * Copyright 2022 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.config; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; +import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.integration.annotation.MessagingGateway; +import org.springframework.integration.gateway.AnnotationGatewayProxyFactoryBean; + +/** + * The {@link InstantiationAwareBeanPostProcessor} to wrap beans for {@link MessagingGateway} + * into {@link AnnotationGatewayProxyFactoryBean}. + * + * @author Artem Bilan + * + * @since 6.0 + * + * @see AnnotationGatewayProxyFactoryBean + */ +class GatewayProxyInstantiationPostProcessor implements + InstantiationAwareBeanPostProcessor, ApplicationContextAware { + + private final BeanDefinitionRegistry registry; + + private ApplicationContext applicationContext; + + GatewayProxyInstantiationPostProcessor(BeanDefinitionRegistry registry) { + this.registry = registry; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { + if (beanClass.isInterface() + && AnnotatedElementUtils.hasAnnotation(beanClass, MessagingGateway.class) + && this.registry.getBeanDefinition(beanName) instanceof AnnotatedGenericBeanDefinition) { + + AnnotationGatewayProxyFactoryBean gatewayProxyFactoryBean = + new AnnotationGatewayProxyFactoryBean<>(beanClass); + gatewayProxyFactoryBean.setApplicationContext(this.applicationContext); + gatewayProxyFactoryBean.setBeanFactory(this.applicationContext.getAutowireCapableBeanFactory()); + ClassLoader classLoader = this.applicationContext.getClassLoader(); + if (classLoader != null) { + gatewayProxyFactoryBean.setBeanClassLoader(classLoader); + } + gatewayProxyFactoryBean.setBeanName(beanName); + return gatewayProxyFactoryBean; + } + return null; + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java index ee2b977659..3cd595328b 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java @@ -63,6 +63,7 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar { if (importingClassMetadata != null) { registerMessagingAnnotationPostProcessors(registry); } + registerGatewayProxyInstantiationPostProcessor(registry); } /** @@ -116,4 +117,15 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar { } } + private void registerGatewayProxyInstantiationPostProcessor(BeanDefinitionRegistry registry) { + if (!registry.containsBeanDefinition("gatewayProxyBeanDefinitionPostProcessor")) { + BeanDefinitionBuilder builder = + BeanDefinitionBuilder.genericBeanDefinition(GatewayProxyInstantiationPostProcessor.class) + .addConstructorArgValue(registry) + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + + registry.registerBeanDefinition("gatewayProxyBeanDefinitionPostProcessor", builder.getBeanDefinition()); + } + } + } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests.java b/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests.java index cf58f957be..cfc0741acd 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests.java @@ -41,6 +41,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.springframework.aop.support.AopUtils; +import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -53,6 +54,7 @@ import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; +import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Profile; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -553,6 +555,26 @@ public class GatewayInterfaceTests { ((SubscribableChannel) this.errorChannel).unsubscribe(messageHandler); } + @Autowired + ImportedGateway importedGateway; + + @Test + void importedGatewayIsProxied() { + assertThat(AopUtils.isAopProxy(this.importedGateway)).isTrue(); + + AnnotationGatewayProxyFactoryBean gatewayProxyFactoryBean = + this.beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + ImportedGateway.class.getName(), + AnnotationGatewayProxyFactoryBean.class); + + assertThat(gatewayProxyFactoryBean.getObjectType()).isEqualTo(ImportedGateway.class); + assertThat(gatewayProxyFactoryBean.getGateways().keySet()) + .hasSize(1) + .extracting("name") + .contains("handle"); + + assertThat(gatewayProxyFactoryBean.getComponentName()).isEqualTo(ImportedGateway.class.getName()); + } + public interface Foo { @Gateway(requestChannel = "requestChannelFoo") @@ -623,6 +645,7 @@ public class GatewayInterfaceTests { @IntegrationComponentScan(useDefaultFilters = false, includeFilters = @ComponentScan.Filter(TestMessagingGateway.class)) @EnableIntegration + @Import(ImportedGateway.class) public static class TestConfig { @Bean(name = IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME) @@ -756,6 +779,13 @@ public class GatewayInterfaceTests { } + @MessagingGateway + public interface ImportedGateway { + + String handle(String payload); + + } + @MessagingGateway(defaultRequestChannel = "errorChannel") @TestMessagingGateway public interface IgnoredHeaderGateway { diff --git a/src/reference/asciidoc/gateway.adoc b/src/reference/asciidoc/gateway.adoc index 8b28162cff..24e0ba515e 100644 --- a/src/reference/asciidoc/gateway.adoc +++ b/src/reference/asciidoc/gateway.adoc @@ -319,6 +319,9 @@ Along with the `@MessagingGateway` annotation you can mark a service interface w Starting with version 6.0, an interface with the `@MessagingGateway` can also be marked with a `@Primary` annotation for respective configuration logic as its possible with any Spring `@Component` definition. +Starting with version 6.0, `@MessagingGateway` interfaces can be used in the standard Spring `@Import` configuration. +This may be used as an alternative to the `@IntegrationComponentScan` or manual `AnnotationGatewayProxyFactoryBean` bean definitions. + NOTE: If you have no XML configuration, the `@EnableIntegration` annotation is required on at least one `@Configuration` class. See <<./overview.adoc#configuration-enable-integration,Configuration and `@EnableIntegration`>> for more information. diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index c2cdd26c93..e4beefc35d 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -114,6 +114,8 @@ The Messaging Gateway interface method can now return `Future` and `Mono> for more information. The `integrationGlobalProperties` bean is now declared by the framework as an instance of `org.springframework.integration.context.IntegrationProperties` instead of the previously deprecated `java.util.Properties`.