GH-3926: BeanNameGenerator for @MessagingGateway (#3927)
* GH-3926: BeanNameGenerator for @MessagingGateway Fixes https://github.com/spring-projects/spring-integration/issues/3926 Current approach for generated bean name in the `MessagingGatewayRegistrar` is to decapitalize simple class name, which is similar to standard `AnnotationBeanNameGenerator` * Make logic in the `MessagingGatewayRegistrar` based on the provided `BeanNameGenerator` * Expose an `@IntegrationComponentScan.nameGenerator()` attribute to allow to customize default bean name generation strategy * Introduce `IntegrationConfigUtils.annotationBeanNameGenerator()` to take a provided `AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR` singleton or fallback to the `AnnotationBeanNameGenerator.INSTANCE` * Use this utility in the `IntegrationComponentScanRegistrar` if no custom strategy is provided in the `@IntegrationComponentScan` * Use same util from the `GatewayParser` since there is no custom naming strategy configuration * Some other current Java level refactoring in the `IntegrationComponentScanRegistrar` and `MessagingGatewayRegistrar` * * Meta-annotate `@MessagingGateway` with a `@MessageEndpoint` * Alias `@MessageEndpoint.value()` with a `@Component.value()` * Alias `@MessagingGateway.name()` with a `@MessageEndpoint.value()` * * Remove unused imports * * Replace `MessagingGatewayRegistrar` parsing logic for annotation configuration directly by the `GatewayProxyInstantiationPostProcessor` and `AnnotationGatewayProxyFactoryBean`. * The `MessagingGatewayRegistrar` logic has been migrated back to the `GatewayParser`, but only with an XML-relevant parts * Change the logic of the `IntegrationComponentScanRegistrar` to rely on a `ClassPathBeanDefinitionScanner` and its `scan()` functionality since this is all what we need to trigger a `GatewayProxyInstantiationPostProcessor` for scanned components * Fix `GatewayProxyInstantiationPostProcessor` to call an `afterPropertiesSet()` as well * Add a `value()` alias attribute for the `@MessagingGateway` to satisfy a name resolution from a `@Component` * Replace annotation chain resolution logic by new `MessagingAnnotationUtils.resolveMergedAttribute()` API * Use `MergedAnnotations` API in the `AnnotationGatewayProxyFactoryBean` to preserve a logic for attribute resolution from the annotation hierarchy * Add expression resolution for attribute values in the `AnnotationGatewayProxyFactoryBean` * Make a `GatewayInterfaceTests.CustomBeanNameGenerator` as an `AnnotationBeanNameGenerator` extension to satisfy the test logic expectations: no custom name if explicit is present * Clean up some doc typos after merge conflict The fix in this commit essentially resolves some old JIRA ticket: https://jira.spring.io/browse/INT-4558 * * Remove redundant `MessagingAnnotationUtils.resolveMergedAttribute()` * Optimize the logic in the `AnnotationGatewayProxyFactoryBean` around annotation attributes to plain annotation - no adaptation to maps
This commit is contained in:
@@ -64,6 +64,7 @@ import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.expression.EnvironmentAccessor;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
import org.springframework.core.serializer.support.SerializingConverter;
|
||||
@@ -488,6 +489,10 @@ public class EnableIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void testMessagingGateway() throws InterruptedException {
|
||||
String gatewayBeanName = AnnotatedElementUtils.findMergedAnnotation(TestGateway.class, Component.class).value();
|
||||
assertThat(gatewayBeanName).isEqualTo("namedTestGateway");
|
||||
assertThat(this.testGateway).isSameAs(this.context.getBean(gatewayBeanName));
|
||||
|
||||
String payload = "bar";
|
||||
String result = this.testGateway.echo(payload);
|
||||
assertThat(result.substring(0, payload.length())).isEqualTo(payload.toUpperCase());
|
||||
@@ -1464,7 +1469,7 @@ public class EnableIntegrationTests {
|
||||
|
||||
}
|
||||
|
||||
@TestMessagingGateway
|
||||
@TestMessagingGateway("namedTestGateway")
|
||||
public interface TestGateway {
|
||||
|
||||
@Gateway(headers = @GatewayHeader(name = "calledMethod", expression = "method.name"))
|
||||
@@ -1494,6 +1499,9 @@ public class EnableIntegrationTests {
|
||||
defaultHeaders = @GatewayHeader(name = "foo", value = "FOO"))
|
||||
public @interface TestMessagingGateway {
|
||||
|
||||
@AliasFor(annotation = MessagingGateway.class, attribute = "value")
|
||||
String value() default "";
|
||||
|
||||
@AliasFor(annotation = MessagingGateway.class, attribute = "defaultRequestChannel")
|
||||
String defaultRequestChannel() default "";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2019 the original author or authors.
|
||||
* Copyright 2016-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.
|
||||
@@ -21,21 +21,21 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.integration.annotation.IntegrationComponentScan;
|
||||
import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.config.IntegrationComponentScanRegistrar;
|
||||
import org.springframework.integration.dsl.flows.IntegrationFlowTests;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
@@ -44,7 +44,7 @@ import org.springframework.util.ClassUtils;
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
public class EnableComponentScanTests {
|
||||
|
||||
@@ -68,13 +68,15 @@ public class EnableComponentScanTests {
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
|
||||
super.registerBeanDefinitions(
|
||||
AnnotationMetadata.introspect(IntegrationComponentScanConfiguration.class), registry);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getBasePackages(AnnotationMetadata importingClassMetadata,
|
||||
protected Collection<String> getBasePackages(AnnotationAttributes importingClassMetadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
|
||||
return Collections.singleton(ClassUtils.getPackageName(IntegrationFlowTests.ControlBusGateway.class));
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,10 @@ 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;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
@@ -94,6 +96,7 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
@@ -121,11 +124,11 @@ public class GatewayInterfaceTests {
|
||||
private NoExecGateway noExecGateway;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("&gatewayInterfaceTests$ExecGateway")
|
||||
@Qualifier("&execGateway")
|
||||
private GatewayProxyFactoryBean<?> execGatewayFB;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("&gatewayInterfaceTests$NoExecGateway")
|
||||
@Qualifier("&noExecutorGateway")
|
||||
private GatewayProxyFactoryBean<?> noExecGatewayFB;
|
||||
|
||||
@Autowired
|
||||
@@ -642,8 +645,10 @@ public class GatewayInterfaceTests {
|
||||
@Configuration
|
||||
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,
|
||||
classes = AutoCreateChannelService.class))
|
||||
@IntegrationComponentScan(useDefaultFilters = false,
|
||||
includeFilters = @ComponentScan.Filter(TestMessagingGateway.class))
|
||||
@IntegrationComponentScan(
|
||||
useDefaultFilters = false,
|
||||
includeFilters = @ComponentScan.Filter(TestMessagingGateway.class),
|
||||
nameGenerator = CustomBeanNameGenerator.class)
|
||||
@EnableIntegration
|
||||
@Import(ImportedGateway.class)
|
||||
public static class TestConfig {
|
||||
@@ -754,7 +759,7 @@ public class GatewayInterfaceTests {
|
||||
|
||||
}
|
||||
|
||||
@MessagingGateway(asyncExecutor = AnnotationConstants.NULL)
|
||||
@MessagingGateway(name = "noExecutorGateway", asyncExecutor = AnnotationConstants.NULL)
|
||||
@TestMessagingGateway
|
||||
public interface NoExecGateway {
|
||||
|
||||
@@ -836,4 +841,20 @@ public class GatewayInterfaceTests {
|
||||
|
||||
}
|
||||
|
||||
public static class CustomBeanNameGenerator extends AnnotationBeanNameGenerator {
|
||||
|
||||
@Override
|
||||
protected String buildDefaultBeanName(BeanDefinition definition) {
|
||||
try {
|
||||
Class<?> beanClass = ClassUtils.forName(definition.getBeanClassName(),
|
||||
ClassUtils.getDefaultClassLoader());
|
||||
return ClassUtils.getShortNameAsProperty(beanClass);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user