Skip to content

Commit 511cb76

Browse files
GH-3923: Add @MessagingGateway @import support (#3929)
* GH-3923: Add @MessagingGateway @import support Fixes #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 <[email protected]> * * Fix bean definition signature Co-authored-by: Gary Russell <[email protected]>
1 parent 532692b commit 511cb76

File tree

5 files changed

+122
-0
lines changed

5 files changed

+122
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.integration.config;
18+
19+
import org.springframework.beans.BeansException;
20+
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
21+
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
22+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
23+
import org.springframework.context.ApplicationContext;
24+
import org.springframework.context.ApplicationContextAware;
25+
import org.springframework.core.annotation.AnnotatedElementUtils;
26+
import org.springframework.integration.annotation.MessagingGateway;
27+
import org.springframework.integration.gateway.AnnotationGatewayProxyFactoryBean;
28+
29+
/**
30+
* The {@link InstantiationAwareBeanPostProcessor} to wrap beans for {@link MessagingGateway}
31+
* into {@link AnnotationGatewayProxyFactoryBean}.
32+
*
33+
* @author Artem Bilan
34+
*
35+
* @since 6.0
36+
*
37+
* @see AnnotationGatewayProxyFactoryBean
38+
*/
39+
class GatewayProxyInstantiationPostProcessor implements
40+
InstantiationAwareBeanPostProcessor, ApplicationContextAware {
41+
42+
private final BeanDefinitionRegistry registry;
43+
44+
private ApplicationContext applicationContext;
45+
46+
GatewayProxyInstantiationPostProcessor(BeanDefinitionRegistry registry) {
47+
this.registry = registry;
48+
}
49+
50+
@Override
51+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
52+
this.applicationContext = applicationContext;
53+
}
54+
55+
@Override
56+
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
57+
if (beanClass.isInterface()
58+
&& AnnotatedElementUtils.hasAnnotation(beanClass, MessagingGateway.class)
59+
&& this.registry.getBeanDefinition(beanName) instanceof AnnotatedGenericBeanDefinition) {
60+
61+
AnnotationGatewayProxyFactoryBean<?> gatewayProxyFactoryBean =
62+
new AnnotationGatewayProxyFactoryBean<>(beanClass);
63+
gatewayProxyFactoryBean.setApplicationContext(this.applicationContext);
64+
gatewayProxyFactoryBean.setBeanFactory(this.applicationContext.getAutowireCapableBeanFactory());
65+
ClassLoader classLoader = this.applicationContext.getClassLoader();
66+
if (classLoader != null) {
67+
gatewayProxyFactoryBean.setBeanClassLoader(classLoader);
68+
}
69+
gatewayProxyFactoryBean.setBeanName(beanName);
70+
return gatewayProxyFactoryBean;
71+
}
72+
return null;
73+
}
74+
75+
}

spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public void registerBeanDefinitions(@Nullable AnnotationMetadata importingClassM
6363
if (importingClassMetadata != null) {
6464
registerMessagingAnnotationPostProcessors(registry);
6565
}
66+
registerGatewayProxyInstantiationPostProcessor(registry);
6667
}
6768

6869
/**
@@ -116,4 +117,15 @@ private void registerMessagingAnnotationPostProcessors(BeanDefinitionRegistry re
116117
}
117118
}
118119

120+
private void registerGatewayProxyInstantiationPostProcessor(BeanDefinitionRegistry registry) {
121+
if (!registry.containsBeanDefinition("gatewayProxyBeanDefinitionPostProcessor")) {
122+
BeanDefinitionBuilder builder =
123+
BeanDefinitionBuilder.genericBeanDefinition(GatewayProxyInstantiationPostProcessor.class)
124+
.addConstructorArgValue(registry)
125+
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
126+
127+
registry.registerBeanDefinition("gatewayProxyBeanDefinitionPostProcessor", builder.getBeanDefinition());
128+
}
129+
}
130+
119131
}

spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.mockito.Mockito;
4242

4343
import org.springframework.aop.support.AopUtils;
44+
import org.springframework.beans.factory.BeanFactory;
4445
import org.springframework.beans.factory.ListableBeanFactory;
4546
import org.springframework.beans.factory.annotation.Autowired;
4647
import org.springframework.beans.factory.annotation.Qualifier;
@@ -53,6 +54,7 @@
5354
import org.springframework.context.annotation.Conditional;
5455
import org.springframework.context.annotation.Configuration;
5556
import org.springframework.context.annotation.FilterType;
57+
import org.springframework.context.annotation.Import;
5658
import org.springframework.context.annotation.Primary;
5759
import org.springframework.context.annotation.Profile;
5860
import org.springframework.context.support.ClassPathXmlApplicationContext;
@@ -553,6 +555,26 @@ void primaryMarkerWins() {
553555
((SubscribableChannel) this.errorChannel).unsubscribe(messageHandler);
554556
}
555557

558+
@Autowired
559+
ImportedGateway importedGateway;
560+
561+
@Test
562+
void importedGatewayIsProxied() {
563+
assertThat(AopUtils.isAopProxy(this.importedGateway)).isTrue();
564+
565+
AnnotationGatewayProxyFactoryBean<?> gatewayProxyFactoryBean =
566+
this.beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + ImportedGateway.class.getName(),
567+
AnnotationGatewayProxyFactoryBean.class);
568+
569+
assertThat(gatewayProxyFactoryBean.getObjectType()).isEqualTo(ImportedGateway.class);
570+
assertThat(gatewayProxyFactoryBean.getGateways().keySet())
571+
.hasSize(1)
572+
.extracting("name")
573+
.contains("handle");
574+
575+
assertThat(gatewayProxyFactoryBean.getComponentName()).isEqualTo(ImportedGateway.class.getName());
576+
}
577+
556578
public interface Foo {
557579

558580
@Gateway(requestChannel = "requestChannelFoo")
@@ -623,6 +645,7 @@ public String service(String request) {
623645
@IntegrationComponentScan(useDefaultFilters = false,
624646
includeFilters = @ComponentScan.Filter(TestMessagingGateway.class))
625647
@EnableIntegration
648+
@Import(ImportedGateway.class)
626649
public static class TestConfig {
627650

628651
@Bean(name = IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME)
@@ -756,6 +779,13 @@ public interface NotAGatewayByScanFilter {
756779

757780
}
758781

782+
@MessagingGateway
783+
public interface ImportedGateway {
784+
785+
String handle(String payload);
786+
787+
}
788+
759789
@MessagingGateway(defaultRequestChannel = "errorChannel")
760790
@TestMessagingGateway
761791
public interface IgnoredHeaderGateway {

src/reference/asciidoc/gateway.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,9 @@ Along with the `@MessagingGateway` annotation you can mark a service interface w
319319

320320
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.
321321

322+
Starting with version 6.0, `@MessagingGateway` interfaces can be used in the standard Spring `@Import` configuration.
323+
This may be used as an alternative to the `@IntegrationComponentScan` or manual `AnnotationGatewayProxyFactoryBean` bean definitions.
324+
322325
NOTE: If you have no XML configuration, the `@EnableIntegration` annotation is required on at least one `@Configuration` class.
323326
See <<./overview.adoc#configuration-enable-integration,Configuration and `@EnableIntegration`>> for more information.
324327

src/reference/asciidoc/whats-new.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ The Messaging Gateway interface method can now return `Future<Void>` and `Mono<V
114114

115115
Alongside with a `@MessagingGateway` annotation the interface can also be marked with a `@Primary`.
116116

117+
`@MessagingGateway` interfaces can now be `@Import`ed into a configuration.
118+
117119
See <<./gateway.adoc#gateway, Messaging Gateway>> for more information.
118120

119121
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`.

0 commit comments

Comments
 (0)