GH-3253: Scan BF hierarchy for BeanDefinition

Fixes https://github.com/spring-projects/spring-integration/issues/3253

The `IntegrationFlowBeanPostProcessor` uses a `containsBean()`
and then `getBeanDefinition()` to be sure that we don't override already
existing bean even if it is created in the parent(s) context.
The problem that `containsBean()` check the hierarchy, but `getBeanDefinition()`
doesn't.
So, we fail with `NoSuchBeanDefinitionException` if bean exists in the parent ctx

* Introduce an utility `IntegrationContextUtils.getBeanDefinition()` to
scan `BeanFactory` recursively for `BeanDefinition` for the requested `name`
* Use this tool in the `IntegrationFlowBeanPostProcessor` logic

**Cherry-pick to 5.2.x & 5.1.x**
This commit is contained in:
Artem Bilan
2020-04-23 17:03:09 -04:00
committed by Gary Russell
parent 8075e157a3
commit a28f5b68a5
2 changed files with 33 additions and 4 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -19,6 +19,9 @@ package org.springframework.integration.context;
import java.util.Properties;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
@@ -214,4 +217,26 @@ public abstract class IntegrationContextUtils {
return properties;
}
/**
* Return a {@link BeanDefinition} with the given name,
* obtained from the given {@link BeanFactory} or one of its parents.
* @param name the bean name to return
* @param beanFactory the {@link ConfigurableListableBeanFactory} to travers.
* @return the {@link BeanDefinition} for a given name
* @throws NoSuchBeanDefinitionException if a {@link BeanDefinition} is not found
* @since 5.1.10
*/
public static BeanDefinition getBeanDefinition(String name, ConfigurableListableBeanFactory beanFactory) {
try {
return beanFactory.getBeanDefinition(name);
}
catch (NoSuchBeanDefinitionException ex) {
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
if (parentBeanFactory instanceof ConfigurableListableBeanFactory) {
return getBeanDefinition(name, (ConfigurableListableBeanFactory) parentBeanFactory);
}
throw ex;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 the original author or authors.
* Copyright 2016-2020 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.
@@ -56,6 +56,7 @@ import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
import org.springframework.integration.config.IntegrationConfigUtils;
import org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.dsl.ComponentsRegistration;
import org.springframework.integration.dsl.ConsumerEndpointSpec;
@@ -282,7 +283,9 @@ public class IntegrationFlowBeanPostProcessor
String beanNameToUse = entry.getValue();
if (StringUtils.hasText(beanNameToUse) &&
ConfigurableBeanFactory.SCOPE_PROTOTYPE.equals(
this.beanFactory.getBeanDefinition(beanNameToUse).getScope())) {
IntegrationContextUtils.getBeanDefinition(beanNameToUse, this.beanFactory)
.getScope())) {
this.beanFactory.initializeBean(componentToUse, beanNameToUse);
}
targetIntegrationComponents.put(component, beanNameToUse);
@@ -392,7 +395,8 @@ public class IntegrationFlowBeanPostProcessor
String beanName = ((NamedComponent) instance).getBeanName();
if (beanName != null) {
if (this.beanFactory.containsBean(beanName)) {
BeanDefinition existingBeanDefinition = this.beanFactory.getBeanDefinition(beanName);
BeanDefinition existingBeanDefinition =
IntegrationContextUtils.getBeanDefinition(beanName, this.beanFactory);
if (!ConfigurableBeanFactory.SCOPE_PROTOTYPE.equals(existingBeanDefinition.getScope())
&& !instance.equals(this.beanFactory.getBean(beanName))) {