GH-3923: Add @MessagingGateway @Import support (#3929)

* GH-3923: Add @MessagingGateway @Import support

Fixes https://github.com/spring-projects/spring-integration/issues/3923

Currently, the `@MessagingGateway` interfaces can be scanned or created
explicitly as `@Bean` definition for `AnnotationGatewayProxyFactoryBean`

* Implement a `GatewayProxyBeanDefinitionPostProcessor` which is invoked
before instantiation attempt on the `BeanDefinition`
* Verify `@Import` for `@MessagingGateway` interface in the `GatewayInterfaceTests`

* Remove supplier for `GatewayProxyInstantiationPostProcessor` bean definition

Co-authored-by: Gary Russell <grussell@vmware.com>

* * Fix bean definition signature

Co-authored-by: Gary Russell <grussell@vmware.com>
This commit is contained in:
Artem Bilan
2022-10-31 10:32:52 -04:00
committed by GitHub
parent 532692b0ec
commit 511cb7619b
5 changed files with 122 additions and 0 deletions

View File

@@ -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;
}
}

View File

@@ -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());
}
}
}

View File

@@ -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 {

View File

@@ -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.

View File

@@ -114,6 +114,8 @@ The Messaging Gateway interface method can now return `Future<Void>` and `Mono<V
Alongside with a `@MessagingGateway` annotation the interface can also be marked with a `@Primary`.
`@MessagingGateway` interfaces can now be `@Import`ed into a configuration.
See <<./gateway.adoc#gateway, Messaging Gateway>> 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`.