Skip to content

Commit c9e3de8

Browse files
committed
GH-9931: Make consumer endpoints dependant on ChannelInitializer
Fixes: #9931 Issue link: #9931 If no message channel bean is declared explicitly, the XML parser for endpoints register such a candidate into the global `ChannelInitializer`. This way, the channel is created when this bean is initialized. However, there are cases when endpoint bean could be called before `ChannelInitializer` initialization. * Add `consumerEndpointBuilder.addDependsOn(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);` into the `AbstractConsumerEndpointParser` to ensure that `ChannelInitializer` bean is initialized before the called endpoint bean. * Adjust `ChannelInitializer` logic to use `registerSingleton()` API instead of `registerBeanDefinition()` since the last one may cause problems with beans cache when this API is called at runtime. **Auto-cherry-pick to `6.4.x`**
1 parent 2cc9686 commit c9e3de8

File tree

2 files changed

+33
-35
lines changed

2 files changed

+33
-35
lines changed

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

+17-27
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,17 +25,18 @@
2525
import org.springframework.beans.factory.BeanFactory;
2626
import org.springframework.beans.factory.BeanFactoryAware;
2727
import org.springframework.beans.factory.InitializingBean;
28-
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
28+
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
29+
import org.springframework.integration.channel.DirectChannel;
2930
import org.springframework.integration.context.IntegrationContextUtils;
3031
import org.springframework.util.Assert;
3132

3233
/**
33-
* A {@link InitializingBean} implementation that is responsible for creating
34+
* An {@link InitializingBean} implementation that is responsible for creating
3435
* channels that are not explicitly defined but identified via the 'input-channel'
3536
* attribute of the corresponding endpoints.
36-
*
37+
* <p>
3738
* This bean plays a role of pre-instantiator since it is instantiated and
38-
* initialized as the very first bean of all SI beans using
39+
* initialized as the very first bean of all Spring Integration beans using
3940
* {@link org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler}.
4041
*
4142
* @author Oleg Zhurakousky
@@ -48,7 +49,7 @@ public final class ChannelInitializer implements BeanFactoryAware, InitializingB
4849

4950
private static final Log LOGGER = LogFactory.getLog(ChannelInitializer.class);
5051

51-
private volatile BeanFactory beanFactory;
52+
private volatile DefaultListableBeanFactory beanFactory;
5253

5354
private volatile boolean autoCreate = true;
5455

@@ -61,49 +62,38 @@ public void setAutoCreate(boolean autoCreate) {
6162

6263
@Override
6364
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
64-
this.beanFactory = beanFactory;
65+
this.beanFactory = (DefaultListableBeanFactory) beanFactory;
6566
}
6667

6768
@Override
6869
public void afterPropertiesSet() {
6970
Assert.notNull(this.beanFactory, "'beanFactory' must not be null");
70-
if (!this.autoCreate) {
71-
return;
72-
}
73-
else {
71+
if (this.autoCreate) {
7472
AutoCreateCandidatesCollector channelCandidatesCollector =
7573
this.beanFactory.getBean(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME,
7674
AutoCreateCandidatesCollector.class);
7775
// at this point channelNames are all resolved with placeholders and SpEL
78-
Collection<String> channelNames = channelCandidatesCollector.getChannelNames();
76+
Collection<String> channelNames = channelCandidatesCollector.channelNames;
7977
if (channelNames != null) {
8078
for (String channelName : channelNames) {
8179
if (!this.beanFactory.containsBean(channelName)) {
8280
if (LOGGER.isDebugEnabled()) {
8381
LOGGER.debug("Auto-creating channel '" + channelName + "' as DirectChannel");
8482
}
85-
IntegrationConfigUtils.autoCreateDirectChannel(channelName,
86-
(BeanDefinitionRegistry) this.beanFactory);
83+
DirectChannel channelToRegister = new DirectChannel();
84+
this.beanFactory.registerSingleton(channelName, channelToRegister);
85+
this.beanFactory.initializeBean(channelToRegister, channelName);
8786
}
8887
}
8988
}
9089
}
9190
}
9291

93-
/*
94-
* Collects candidate channel names to be auto-created by ChannelInitializer
92+
/**
93+
* Collects candidate channel names to be auto-created by {@link ChannelInitializer}.
94+
* @param channelNames the auto-create candidate channel bean names.
9595
*/
96-
public static class AutoCreateCandidatesCollector {
97-
98-
private final Collection<String> channelNames;
99-
100-
AutoCreateCandidatesCollector(Collection<String> channelNames) {
101-
this.channelNames = channelNames;
102-
}
103-
104-
public Collection<String> getChannelNames() {
105-
return this.channelNames;
106-
}
96+
public record AutoCreateCandidatesCollector(Collection<String> channelNames) {
10797

10898
}
10999

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

+16-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
2929
import org.springframework.beans.factory.support.AbstractBeanDefinition;
3030
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
3131
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
32+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
3233
import org.springframework.beans.factory.support.ManagedList;
3334
import org.springframework.beans.factory.support.ManagedSet;
3435
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
@@ -148,7 +149,7 @@ protected final AbstractBeanDefinition parseInternal(Element element, ParserCont
148149
String inputChannelName = element.getAttribute(inputChannelAttributeName);
149150

150151
if (!parserContext.getRegistry().containsBeanDefinition(inputChannelName)) {
151-
registerChannelForCreation(parserContext, inputChannelName);
152+
registerChannelForCreation(parserContext, inputChannelName, builder);
152153
}
153154
IntegrationNamespaceUtils.checkAndConfigureFixedSubscriberChannel(element, parserContext, inputChannelName,
154155
handlerBeanName);
@@ -177,12 +178,17 @@ private void poller(Element element, ParserContext parserContext, BeanDefinition
177178
}
178179
}
179180

180-
private void registerChannelForCreation(ParserContext parserContext, String inputChannelName) {
181-
if (parserContext.getRegistry()
182-
.containsBeanDefinition(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)) {
181+
private void registerChannelForCreation(ParserContext parserContext, String inputChannelName,
182+
BeanDefinitionBuilder consumerEndpointBuilder) {
183183

184-
BeanDefinition channelRegistry = parserContext.getRegistry().
185-
getBeanDefinition(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
184+
BeanDefinitionRegistry beanDefinitionRegistry = parserContext.getRegistry();
185+
186+
if (beanDefinitionRegistry.containsBeanDefinition(
187+
IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)) {
188+
189+
BeanDefinition channelRegistry =
190+
beanDefinitionRegistry.getBeanDefinition(
191+
IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
186192
ConstructorArgumentValues caValues = channelRegistry.getConstructorArgumentValues();
187193
ValueHolder vh = caValues.getArgumentValue(0, Collection.class);
188194
if (vh == null) { //although it should never happen if it does we can fix it
@@ -194,11 +200,13 @@ private void registerChannelForCreation(ParserContext parserContext, String inpu
194200
(Collection<String>) caValues.getArgumentValue(0, Collection.class)
195201
.getValue(); // NOSONAR see comment above
196202
channelCandidateNames.add(inputChannelName); // NOSONAR
203+
204+
consumerEndpointBuilder.addDependsOn(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
197205
}
198206
else {
199207
parserContext.getReaderContext().error("Failed to locate '" +
200208
IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME + "'",
201-
parserContext.getRegistry());
209+
beanDefinitionRegistry);
202210
}
203211
}
204212

0 commit comments

Comments
 (0)