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:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user