diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/IntegrationComponentScan.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/IntegrationComponentScan.java
index 6f4e0cfda6..3acf78723d 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/annotation/IntegrationComponentScan.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/IntegrationComponentScan.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2019 the original author or authors.
+ * Copyright 2014-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.
@@ -22,6 +22,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.AliasFor;
@@ -105,4 +106,17 @@ public @interface IntegrationComponentScan {
*/
Filter[] excludeFilters() default { };
+
+ /**
+ * The {@link BeanNameGenerator} class to be used for naming detected Spring Integration components.
+ *
The default value of the {@link BeanNameGenerator} interface itself indicates
+ * that the scanner used to process this {@link IntegrationComponentScan} annotation should
+ * use its inherited bean name generator, e.g. the default
+ * {@link org.springframework.context.annotation.AnnotationBeanNameGenerator}
+ * or any custom instance supplied to the application context at bootstrap time.
+ * @since 6.0
+ * @see org.springframework.context.annotation.ComponentScan#nameGenerator()
+ */
+ Class extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
+
}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/MessageEndpoint.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/MessageEndpoint.java
index 8e26ed51d3..37221e247c 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/annotation/MessageEndpoint.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/MessageEndpoint.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-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.
@@ -23,6 +23,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
/**
@@ -30,6 +31,7 @@ import org.springframework.stereotype.Component;
* Message Endpoint.
*
* @author Mark Fisher
+ * @author Artem Bilan
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@@ -41,9 +43,9 @@ public @interface MessageEndpoint {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
- *
* @return the suggested component name, if any
*/
+ @AliasFor(annotation = Component.class)
String value() default "";
}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/annotation/MessagingGateway.java b/spring-integration-core/src/main/java/org/springframework/integration/annotation/MessagingGateway.java
index 53419dab49..d05a16f273 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/annotation/MessagingGateway.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/annotation/MessagingGateway.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2021 the original author or authors.
+ * Copyright 2014-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,12 +21,12 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import org.springframework.stereotype.Indexed;
+import org.springframework.core.annotation.AliasFor;
/**
* A stereotype annotation to provide an Integration Messaging Gateway Proxy
- * ({@code }) as an abstraction over the messaging API. The target
- * application's business logic may be completely unaware of the Spring Integration
+ * as an abstraction over the messaging API. The target application's
+ * business logic may be completely unaware of the Spring Integration
* API, with the code interacting only via the interface.
*
* Important: The {@link IntegrationComponentScan} annotation is required along with
@@ -41,17 +41,28 @@ import org.springframework.stereotype.Indexed;
* @since 4.0
*
* @see IntegrationComponentScan
+ * @see MessageEndpoint
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
-@Indexed
+@MessageEndpoint
public @interface MessagingGateway {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any
+ * @since 6.0
*/
+ @AliasFor(annotation = MessageEndpoint.class)
+ String value() default "";
+
+ /**
+ * The value may indicate a suggestion for a logical component name,
+ * to be turned into a Spring bean in case of an autodetected component.
+ * @return the suggested component name, if any
+ */
+ @AliasFor(annotation = MessageEndpoint.class, attribute = "value")
String name() default "";
/**
@@ -90,7 +101,7 @@ public @interface MessagingGateway {
/**
* Allows to specify how long this gateway will wait for the reply {@code Message}
- * before returning. By default it will wait indefinitely. {@code null} is returned if
+ * before returning. By default, it will wait indefinitely. {@code null} is returned if
* the gateway times out. Value is specified in milliseconds; it can be a simple long
* value or a SpEL expression; array variable #args is available.
* @return the suggested timeout in milliseconds, if any
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
index 07d596b05f..6d743e2bcc 100644
--- 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
@@ -18,10 +18,12 @@ package org.springframework.integration.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinition;
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.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.gateway.AnnotationGatewayProxyFactoryBean;
@@ -54,20 +56,23 @@ class GatewayProxyInstantiationPostProcessor implements
@Override
public Object postProcessBeforeInstantiation(Class> beanClass, String beanName) throws BeansException {
- if (beanClass.isInterface()
- && AnnotatedElementUtils.hasAnnotation(beanClass, MessagingGateway.class)
- && this.registry.getBeanDefinition(beanName) instanceof AnnotatedGenericBeanDefinition) {
+ if (beanClass.isInterface() && AnnotatedElementUtils.hasAnnotation(beanClass, MessagingGateway.class)) {
+ BeanDefinition beanDefinition = this.registry.getBeanDefinition(beanName);
+ if (beanDefinition instanceof AnnotatedGenericBeanDefinition
+ || beanDefinition instanceof ScannedGenericBeanDefinition) {
- 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);
+ 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);
+ gatewayProxyFactoryBean.afterPropertiesSet();
+ return gatewayProxyFactoryBean;
}
- gatewayProxyFactoryBean.setBeanName(beanName);
- return gatewayProxyFactoryBean;
}
return null;
}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationComponentScanRegistrar.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationComponentScanRegistrar.java
index 32c5c19fc7..09e5fb0f14 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationComponentScanRegistrar.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationComponentScanRegistrar.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014-2019 the original author or authors.
+ * Copyright 2014-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.
@@ -20,10 +20,8 @@ import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
@@ -33,11 +31,12 @@ import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
-import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
+import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
@@ -67,15 +66,14 @@ import org.springframework.util.StringUtils;
public class IntegrationComponentScanRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, EnvironmentAware {
- private final Map componentRegistrars = new HashMap<>();
+ private final List defaultFilters = new ArrayList<>();
private ResourceLoader resourceLoader;
private Environment environment;
public IntegrationComponentScanRegistrar() {
- this.componentRegistrars.put(new AnnotationTypeFilter(MessagingGateway.class, true),
- new MessagingGatewayRegistrar());
+ this.defaultFilters.add(new AnnotationTypeFilter(MessagingGateway.class, true));
}
@Override
@@ -90,84 +88,75 @@ public class IntegrationComponentScanRegistrar implements ImportBeanDefinitionRe
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
- Map componentScan =
- importingClassMetadata.getAnnotationAttributes(IntegrationComponentScan.class.getName());
+ AnnotationAttributes componentScan =
+ AnnotationAttributes.fromMap(
+ importingClassMetadata.getAnnotationAttributes(IntegrationComponentScan.class.getName()));
- Collection basePackages = getBasePackages(importingClassMetadata, registry);
+ Collection basePackages = getBasePackages(componentScan, registry);
if (basePackages.isEmpty()) {
basePackages = Collections.singleton(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
- ClassPathScanningCandidateComponentProvider scanner =
- new ClassPathScanningCandidateComponentProvider(false, this.environment) {
+ ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false) {
- @Override
- protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
- return beanDefinition.getMetadata().isIndependent()
- && !beanDefinition.getMetadata().isAnnotation();
- }
+ @Override
+ protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
+ return beanDefinition.getMetadata().isIndependent()
+ && !beanDefinition.getMetadata().isAnnotation();
+ }
- @Override
- protected BeanDefinitionRegistry getRegistry() {
- return registry;
- }
-
- };
+ };
filter(registry, componentScan, scanner); // NOSONAR - never null
scanner.setResourceLoader(this.resourceLoader);
+ scanner.setEnvironment(this.environment);
- for (String basePackage : basePackages) {
- Set candidateComponents = scanner.findCandidateComponents(basePackage);
- for (BeanDefinition candidateComponent : candidateComponents) {
- if (candidateComponent instanceof AnnotatedBeanDefinition) {
- for (ImportBeanDefinitionRegistrar registrar : this.componentRegistrars.values()) {
- registrar.registerBeanDefinitions(((AnnotatedBeanDefinition) candidateComponent).getMetadata(),
- registry);
- }
- }
- }
+ BeanNameGenerator beanNameGenerator = IntegrationConfigUtils.annotationBeanNameGenerator(registry);
+
+ Class extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
+ if (!(BeanNameGenerator.class == generatorClass)) {
+ beanNameGenerator = BeanUtils.instantiateClass(generatorClass);
}
+
+ scanner.setBeanNameGenerator(beanNameGenerator);
+ scanner.scan(basePackages.toArray(String[]::new));
}
- private void filter(BeanDefinitionRegistry registry, Map componentScan,
+ private void filter(BeanDefinitionRegistry registry, AnnotationAttributes componentScan,
ClassPathScanningCandidateComponentProvider scanner) {
- if ((boolean) componentScan.get("useDefaultFilters")) { // NOSONAR - never null
- for (TypeFilter typeFilter : this.componentRegistrars.keySet()) {
+ if (componentScan.getBoolean("useDefaultFilters")) { // NOSONAR - never null
+ for (TypeFilter typeFilter : this.defaultFilters) {
scanner.addIncludeFilter(typeFilter);
}
}
- for (AnnotationAttributes filter : (AnnotationAttributes[]) componentScan.get("includeFilters")) {
+ for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter, registry)) {
scanner.addIncludeFilter(typeFilter);
}
}
- for (AnnotationAttributes filter : (AnnotationAttributes[]) componentScan.get("excludeFilters")) {
+ for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter, registry)) {
scanner.addExcludeFilter(typeFilter);
}
}
}
- protected Collection getBasePackages(AnnotationMetadata importingClassMetadata,
+ protected Collection getBasePackages(AnnotationAttributes componentScan,
@SuppressWarnings("unused") BeanDefinitionRegistry registry) {
- Map componentScan =
- importingClassMetadata.getAnnotationAttributes(IntegrationComponentScan.class.getName());
-
Set basePackages = new HashSet<>();
- for (String pkg : (String[]) componentScan.get("value")) { // NOSONAR - never null
+ for (String pkg : componentScan.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
- for (Class> clazz : (Class>[]) componentScan.get("basePackageClasses")) {
+ for (Class> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
@@ -180,38 +169,33 @@ public class IntegrationComponentScanRegistrar implements ImportBeanDefinitionRe
for (Class> filterClass : filter.getClassArray("classes")) {
switch (filterType) {
- case ANNOTATION:
+ case ANNOTATION -> {
Assert.isAssignable(Annotation.class, filterClass,
"An error occurred while processing a @IntegrationComponentScan ANNOTATION type filter: ");
@SuppressWarnings("unchecked")
Class annotationType = (Class) filterClass;
typeFilters.add(new AnnotationTypeFilter(annotationType));
- break;
- case ASSIGNABLE_TYPE:
- typeFilters.add(new AssignableTypeFilter(filterClass));
- break;
- case CUSTOM:
+ }
+ case ASSIGNABLE_TYPE -> typeFilters.add(new AssignableTypeFilter(filterClass));
+ case CUSTOM -> {
Assert.isAssignable(TypeFilter.class, filterClass,
"An error occurred while processing a @IntegrationComponentScan CUSTOM type filter: ");
TypeFilter typeFilter = BeanUtils.instantiateClass(filterClass, TypeFilter.class);
invokeAwareMethods(filter, this.environment, this.resourceLoader, registry);
typeFilters.add(typeFilter);
- break;
- default:
- throw new IllegalArgumentException("Filter type not supported with Class value: " + filterType);
+ }
+ default -> throw new IllegalArgumentException("Filter type not supported with Class value: " +
+ filterType);
}
}
for (String expression : filter.getStringArray("pattern")) {
switch (filterType) {
- case ASPECTJ:
- typeFilters.add(new AspectJTypeFilter(expression, this.resourceLoader.getClassLoader()));
- break;
- case REGEX:
- typeFilters.add(new RegexPatternTypeFilter(Pattern.compile(expression)));
- break;
- default:
- throw new IllegalArgumentException("Filter type not supported with String pattern: " + filterType);
+ case ASPECTJ ->
+ typeFilters.add(new AspectJTypeFilter(expression, this.resourceLoader.getClassLoader()));
+ case REGEX -> typeFilters.add(new RegexPatternTypeFilter(Pattern.compile(expression)));
+ default -> throw new IllegalArgumentException("Filter type not supported with String pattern: "
+ + filterType);
}
}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationConfigUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationConfigUtils.java
index 117ead890d..a7bce6c1ca 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationConfigUtils.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationConfigUtils.java
@@ -16,9 +16,13 @@
package org.springframework.integration.config;
+import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.annotation.AnnotationBeanNameGenerator;
+import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.integration.channel.DirectChannel;
/**
@@ -64,6 +68,20 @@ public final class IntegrationConfigUtils {
registry.registerBeanDefinition(channelName, new RootBeanDefinition(DirectChannel.class, DirectChannel::new));
}
+ public static BeanNameGenerator annotationBeanNameGenerator(BeanDefinitionRegistry registry) {
+ BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;
+ if (registry instanceof SingletonBeanRegistry singletonBeanRegistry) {
+ BeanNameGenerator generator =
+ (BeanNameGenerator) singletonBeanRegistry.getSingleton(
+ AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
+ if (generator != null) {
+ beanNameGenerator = generator;
+ }
+ }
+
+ return beanNameGenerator;
+ }
+
private IntegrationConfigUtils() {
}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/MessagingGatewayRegistrar.java b/spring-integration-core/src/main/java/org/springframework/integration/config/MessagingGatewayRegistrar.java
deleted file mode 100644
index 2ad7f7bee8..0000000000
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/MessagingGatewayRegistrar.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright 2014-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 java.beans.Introspector;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import org.springframework.beans.factory.BeanDefinitionStoreException;
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.beans.factory.config.BeanDefinitionHolder;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
-import org.springframework.beans.factory.support.BeanDefinitionRegistry;
-import org.springframework.beans.factory.support.ManagedMap;
-import org.springframework.beans.factory.support.RootBeanDefinition;
-import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
-import org.springframework.context.annotation.Primary;
-import org.springframework.core.ResolvableType;
-import org.springframework.core.type.AnnotationMetadata;
-import org.springframework.expression.common.LiteralExpression;
-import org.springframework.integration.annotation.AnnotationConstants;
-import org.springframework.integration.annotation.MessagingGateway;
-import org.springframework.integration.gateway.GatewayMethodMetadata;
-import org.springframework.integration.gateway.GatewayProxyFactoryBean;
-import org.springframework.integration.gateway.RequestReplyExchanger;
-import org.springframework.integration.util.MessagingAnnotationUtils;
-import org.springframework.util.Assert;
-import org.springframework.util.ClassUtils;
-import org.springframework.util.MultiValueMap;
-import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
-
-/**
- * The {@link ImportBeanDefinitionRegistrar} to parse {@link MessagingGateway} and its {@code service-interface}
- * and to register {@link BeanDefinition} {@link GatewayProxyFactoryBean}.
- *
- * @author Artem Bilan
- * @author Gary Russell
- * @author Andy Wilksinson
- *
- * @since 4.0
- */
-public class MessagingGatewayRegistrar implements ImportBeanDefinitionRegistrar {
-
- private static final String PROXY_DEFAULT_METHODS_ATTR = "proxyDefaultMethods";
-
- private static final String PRIMARY_ATTR = "primary";
-
- @Override
- public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
- if (importingClassMetadata != null && importingClassMetadata.isAnnotated(MessagingGateway.class.getName())) {
- Assert.isTrue(importingClassMetadata.isInterface(),
- "@MessagingGateway can only be specified on an interface");
- List> valuesHierarchy = captureMetaAnnotationValues(importingClassMetadata);
- Map annotationAttributes =
- importingClassMetadata.getAnnotationAttributes(MessagingGateway.class.getName());
- replaceEmptyOverrides(valuesHierarchy, annotationAttributes); // NOSONAR never null
- annotationAttributes.put("serviceInterface", importingClassMetadata.getClassName());
- annotationAttributes.put(PROXY_DEFAULT_METHODS_ATTR,
- "" + annotationAttributes.remove(PROXY_DEFAULT_METHODS_ATTR));
- if (importingClassMetadata.isAnnotated(Primary.class.getName())) {
- annotationAttributes.put(PRIMARY_ATTR, true);
- }
- BeanDefinitionReaderUtils.registerBeanDefinition(parse(annotationAttributes, registry), registry);
- }
- }
-
- public BeanDefinitionHolder parse(Map gatewayAttributes, BeanDefinitionRegistry registry) { // NOSONAR complexity
- String defaultPayloadExpression = (String) gatewayAttributes.get("defaultPayloadExpression");
-
- @SuppressWarnings("unchecked")
- Map[] defaultHeaders = (Map[]) gatewayAttributes.get("defaultHeaders");
-
- String defaultRequestChannel = (String) gatewayAttributes.get("defaultRequestChannel");
- String defaultReplyChannel = (String) gatewayAttributes.get("defaultReplyChannel");
- String errorChannel = (String) gatewayAttributes.get("errorChannel");
- String asyncExecutor = (String) gatewayAttributes.get("asyncExecutor");
- String mapper = (String) gatewayAttributes.get("mapper");
- String proxyDefaultMethods = (String) gatewayAttributes.get(PROXY_DEFAULT_METHODS_ATTR);
-
- boolean hasMapper = StringUtils.hasText(mapper);
- boolean hasDefaultPayloadExpression = StringUtils.hasText(defaultPayloadExpression);
- Assert.state(!hasMapper || !hasDefaultPayloadExpression,
- "'defaultPayloadExpression' is not allowed when a 'mapper' is provided");
-
- boolean hasDefaultHeaders = !ObjectUtils.isEmpty(defaultHeaders);
- Assert.state(!hasMapper || !hasDefaultHeaders,
- "'defaultHeaders' are not allowed when a 'mapper' is provided");
-
- ConfigurableBeanFactory beanFactory = obtainBeanFactory(registry);
- Class> serviceInterface = getServiceInterface((String) gatewayAttributes.get("serviceInterface"), beanFactory);
-
- BeanDefinitionBuilder gatewayProxyBuilder =
- BeanDefinitionBuilder.rootBeanDefinition(GatewayProxyFactoryBean.class,
- () -> new GatewayProxyFactoryBean<>(serviceInterface));
-
- if (hasDefaultHeaders || hasDefaultPayloadExpression) {
- BeanDefinitionBuilder methodMetadataBuilder =
- BeanDefinitionBuilder.genericBeanDefinition(GatewayMethodMetadata.class, GatewayMethodMetadata::new);
-
- if (hasDefaultPayloadExpression) {
- methodMetadataBuilder.addPropertyValue("payloadExpression",
- BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class)
- .addConstructorArgValue(defaultPayloadExpression)
- .getBeanDefinition());
- }
-
- if (hasDefaultHeaders) {
- Map headerExpressions = new ManagedMap<>();
- for (Map header : defaultHeaders) {
- String headerValue = (String) header.get("value");
- String headerExpression = (String) header.get("expression");
- boolean hasValue = StringUtils.hasText(headerValue);
-
- if (hasValue == StringUtils.hasText(headerExpression)) {
- throw new BeanDefinitionStoreException("exactly one of 'value' or 'expression' " +
- "is required on a gateway's header.");
- }
-
- BeanDefinition expressionDef =
- new RootBeanDefinition(hasValue ? LiteralExpression.class : ExpressionFactoryBean.class);
- expressionDef.getConstructorArgumentValues()
- .addGenericArgumentValue(hasValue ? headerValue : headerExpression);
-
- headerExpressions.put((String) header.get("name"), expressionDef);
- }
- methodMetadataBuilder.addPropertyValue("headerExpressions", headerExpressions);
- }
-
- gatewayProxyBuilder.addPropertyValue("globalMethodMetadata", methodMetadataBuilder.getBeanDefinition());
- }
-
-
- if (StringUtils.hasText(defaultRequestChannel)) {
- gatewayProxyBuilder.addPropertyValue("defaultRequestChannelName", defaultRequestChannel);
- }
- if (StringUtils.hasText(defaultReplyChannel)) {
- gatewayProxyBuilder.addPropertyValue("defaultReplyChannelName", defaultReplyChannel);
- }
- if (StringUtils.hasText(errorChannel)) {
- gatewayProxyBuilder.addPropertyValue("errorChannelName", errorChannel);
- }
- if (asyncExecutor == null || AnnotationConstants.NULL.equals(asyncExecutor)) {
- gatewayProxyBuilder.addPropertyValue("asyncExecutor", null);
- }
- else if (StringUtils.hasText(asyncExecutor)) {
- gatewayProxyBuilder.addPropertyReference("asyncExecutor", asyncExecutor);
- }
- if (StringUtils.hasText(mapper)) {
- gatewayProxyBuilder.addPropertyReference("mapper", mapper);
- }
- if (StringUtils.hasText(proxyDefaultMethods)) {
- gatewayProxyBuilder.addPropertyValue(PROXY_DEFAULT_METHODS_ATTR, proxyDefaultMethods);
- }
-
- gatewayProxyBuilder.addPropertyValue("defaultRequestTimeoutExpressionString",
- gatewayAttributes.get("defaultRequestTimeout"));
- gatewayProxyBuilder.addPropertyValue("defaultReplyTimeoutExpressionString",
- gatewayAttributes.get("defaultReplyTimeout"));
- gatewayProxyBuilder.addPropertyValue("methodMetadataMap", gatewayAttributes.get("methods"));
-
-
- String id = (String) gatewayAttributes.get("name");
- if (!StringUtils.hasText(id)) {
- String serviceInterfaceName = serviceInterface.getName();
- id = Introspector.decapitalize(serviceInterfaceName.substring(serviceInterfaceName.lastIndexOf('.') + 1));
- }
-
- gatewayProxyBuilder.addConstructorArgValue(serviceInterface);
- gatewayProxyBuilder.setPrimary(gatewayAttributes.containsKey(PRIMARY_ATTR));
-
- RootBeanDefinition beanDefinition = (RootBeanDefinition) gatewayProxyBuilder.getBeanDefinition();
- beanDefinition.setTargetType(
- ResolvableType.forClassWithGenerics(GatewayProxyFactoryBean.class, serviceInterface));
- return new BeanDefinitionHolder(beanDefinition, id);
- }
-
- /**
- * TODO until SPR-11710 will be resolved.
- * Captures the meta-annotation attribute values, in order.
- * @param importingClassMetadata The importing class metadata
- * @return The captured values.
- */
- private static List> captureMetaAnnotationValues(
- AnnotationMetadata importingClassMetadata) {
-
- Set directAnnotations = importingClassMetadata.getAnnotationTypes();
- List> valuesHierarchy = new ArrayList<>();
- // Need to grab the values now; see SPR-11710
- for (String ann : directAnnotations) {
- Set chain = importingClassMetadata.getMetaAnnotationTypes(ann);
- if (chain.contains(MessagingGateway.class.getName())) {
- for (String meta : chain) {
- MultiValueMap attributes = importingClassMetadata.getAllAnnotationAttributes(meta);
- if (attributes != null) {
- valuesHierarchy.add(attributes);
- }
- }
- }
- }
- return valuesHierarchy;
- }
-
- /**
- * TODO until SPR-11709 will be resolved.
- * For any empty values, traverses back up the meta-annotation hierarchy to
- * see if a value has been overridden to empty, and replaces the first such value found.
- * @param valuesHierarchy The values hierarchy in order.
- * @param annotationAttributes The current attribute values.
- */
- private static void replaceEmptyOverrides(List> valuesHierarchy,
- Map annotationAttributes) {
-
- for (Entry entry : annotationAttributes.entrySet()) {
- Object value = entry.getValue();
- if (!MessagingAnnotationUtils.hasValue(value)) {
- // see if we overrode a value that was higher in the annotation chain
- for (MultiValueMap metaAttributesMap : valuesHierarchy) {
- Object newValue = metaAttributesMap.getFirst(entry.getKey());
- if (MessagingAnnotationUtils.hasValue(newValue)) {
- annotationAttributes.put(entry.getKey(), newValue);
- break;
- }
- }
- }
- }
- }
-
- private static ConfigurableBeanFactory obtainBeanFactory(BeanDefinitionRegistry registry) {
- if (registry instanceof ConfigurableBeanFactory) {
- return (ConfigurableBeanFactory) registry;
- }
- else if (registry instanceof ConfigurableApplicationContext) {
- return ((ConfigurableApplicationContext) registry).getBeanFactory();
- }
- throw new IllegalArgumentException("The provided 'BeanDefinitionRegistry' must be an instance " +
- "of 'ConfigurableBeanFactory' or 'ConfigurableApplicationContext', but given is: "
- + registry.getClass());
- }
-
- private static Class> getServiceInterface(String serviceInterface, ConfigurableBeanFactory beanFactory) {
- String actualServiceInterface = beanFactory.resolveEmbeddedValue(serviceInterface);
- if (!StringUtils.hasText(actualServiceInterface)) {
- return RequestReplyExchanger.class;
- }
- try {
- return ClassUtils.forName(actualServiceInterface, beanFactory.getBeanClassLoader());
- }
- catch (ClassNotFoundException ex) {
- throw new BeanDefinitionStoreException("Cannot parse class for service interface", ex);
- }
- }
-
-}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GatewayParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GatewayParser.java
index 486e64e542..4dc3c48fe8 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GatewayParser.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/GatewayParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-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.
@@ -23,19 +23,33 @@ import java.util.Map;
import org.w3c.dom.Element;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.ManagedMap;
+import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.ResolvableType;
+import org.springframework.expression.common.LiteralExpression;
+import org.springframework.integration.annotation.AnnotationConstants;
import org.springframework.integration.config.ExpressionFactoryBean;
-import org.springframework.integration.config.MessagingGatewayRegistrar;
+import org.springframework.integration.config.IntegrationConfigUtils;
import org.springframework.integration.gateway.GatewayMethodMetadata;
+import org.springframework.integration.gateway.GatewayProxyFactoryBean;
+import org.springframework.integration.gateway.RequestReplyExchanger;
import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
@@ -49,7 +63,7 @@ import org.springframework.util.xml.DomUtils;
*/
public class GatewayParser implements BeanDefinitionParser {
- private final MessagingGatewayRegistrar registrar = new MessagingGatewayRegistrar();
+ private static final String PROXY_DEFAULT_METHODS_ATTR = "proxyDefaultMethods";
@Override
public BeanDefinition parse(final Element element, ParserContext parserContext) {
@@ -88,7 +102,7 @@ public class GatewayParser implements BeanDefinitionParser {
gatewayAttributes.put("proxyDefaultMethods", element.getAttribute("proxy-default-methods"));
- BeanDefinitionHolder gatewayHolder = this.registrar.parse(gatewayAttributes, parserContext.getRegistry());
+ BeanDefinitionHolder gatewayHolder = buildBeanDefinition(gatewayAttributes, parserContext);
if (isNested) {
return gatewayHolder.getBeanDefinition();
}
@@ -98,7 +112,7 @@ public class GatewayParser implements BeanDefinitionParser {
}
}
- private void headers(Element element, Map gatewayAttributes) {
+ private static void headers(Element element, Map gatewayAttributes) {
List headerElements = DomUtils.getChildElementsByTagName(element, "default-header");
if (!CollectionUtils.isEmpty(headerElements)) {
List